diff --git a/Libs/UnityEngine.dll b/Libs/UnityEngine.dll deleted file mode 100644 index 80cf67e..0000000 Binary files a/Libs/UnityEngine.dll and /dev/null differ diff --git a/Libs/websocket-sharp.dll b/Libs/websocket-sharp.dll deleted file mode 100644 index 49661e8..0000000 Binary files a/Libs/websocket-sharp.dll and /dev/null differ diff --git a/LiveQuery/LiveQuery.PCL/LiveQuery.PCL.csproj b/LiveQuery/LiveQuery.PCL/LiveQuery.PCL.csproj deleted file mode 100644 index c6dd636..0000000 --- a/LiveQuery/LiveQuery.PCL/LiveQuery.PCL.csproj +++ /dev/null @@ -1,53 +0,0 @@ - - - - Debug - AnyCPU - {EA1C601E-D853-41F7-B9EB-276CBF7D1FA5} - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Library - LiveQuery.PCL - LiveQuery.PCL - v4.5 - Profile111 - 0.1.0 - - - true - full - false - bin\Debug - DEBUG; - prompt - 4 - - - true - bin\Release - prompt - 4 - - - - - Source\AVLiveQuery.cs - - - Source\AVLiveQueryEventArgs.cs - - - Source\AVLiveQueryExtensions.cs - - - - - {92B2B40E-A3CD-4672-AC84-2E894E1A6CE5} - RTM.PCL - - - {659D19F0-9A40-42C0-886C-555E64F16848} - Storage.PCL - - - - \ No newline at end of file diff --git a/LiveQuery/LiveQuery.PCL/Properties/AssemblyInfo.cs b/LiveQuery/LiveQuery.PCL/Properties/AssemblyInfo.cs deleted file mode 100644 index 5cf7adb..0000000 --- a/LiveQuery/LiveQuery.PCL/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. - -[assembly: AssemblyTitle("LiveQuery.PCL")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("")] -[assembly: AssemblyCopyright("${AuthorCopyright}")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. - -[assembly: AssemblyVersion("1.0.*")] - -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. - -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] diff --git a/LiveQuery/LiveQuery.Test/LiveQuery.Test.csproj b/LiveQuery/LiveQuery.Test/LiveQuery.Test.csproj deleted file mode 100644 index c382c64..0000000 --- a/LiveQuery/LiveQuery.Test/LiveQuery.Test.csproj +++ /dev/null @@ -1,41 +0,0 @@ - - - - Debug - AnyCPU - {F907012C-74DF-4575-AFE6-E8DAACC26D24} - Library - LiveQuery.Test - LiveQuery.Test - v4.7 - 0.1.0 - - - true - full - false - bin\Debug - DEBUG; - prompt - 4 - - - true - bin\Release - prompt - 4 - - - - - ..\..\packages\NUnit.2.6.4\lib\nunit.framework.dll - - - - - - - - - - \ No newline at end of file diff --git a/LiveQuery/LiveQuery.Test/Test.cs b/LiveQuery/LiveQuery.Test/Test.cs deleted file mode 100644 index 9bd89d3..0000000 --- a/LiveQuery/LiveQuery.Test/Test.cs +++ /dev/null @@ -1,10 +0,0 @@ -using NUnit.Framework; -using System; -namespace LiveQuery.Test { - [TestFixture()] - public class Test { - [Test()] - public void TestCase() { - } - } -} diff --git a/LiveQuery/LiveQuery.Test/packages.config b/LiveQuery/LiveQuery.Test/packages.config deleted file mode 100644 index bbb222f..0000000 --- a/LiveQuery/LiveQuery.Test/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/LiveQuery/LiveQuery.Unity/LiveQuery.Unity.csproj b/LiveQuery/LiveQuery.Unity/LiveQuery.Unity.csproj deleted file mode 100644 index 2e35ef5..0000000 --- a/LiveQuery/LiveQuery.Unity/LiveQuery.Unity.csproj +++ /dev/null @@ -1,56 +0,0 @@ - - - - Debug - AnyCPU - {3251B4D8-D11A-4D90-8626-27FEE266B066} - Library - LiveQuery.Unity - LiveQuery.Unity - v4.7 - 0.1.0 - - - true - full - false - bin\Debug - DEBUG; - prompt - 4 - false - - - true - bin\Release - prompt - 4 - false - - - - - - - - Source\AVLiveQuery.cs - - - Source\AVLiveQueryEventArgs.cs - - - Source\AVLiveQueryExtensions.cs - - - - - {1E608FCD-9039-4FF7-8EE7-BA8B00E15D1C} - RTM.Unity - - - {A0D50BCB-E50E-4AAE-8E7D-24BF5AE33DAC} - Storage.Unity - - - - \ No newline at end of file diff --git a/LiveQuery/LiveQuery.Unity/Properties/AssemblyInfo.cs b/LiveQuery/LiveQuery.Unity/Properties/AssemblyInfo.cs deleted file mode 100644 index e93ac80..0000000 --- a/LiveQuery/LiveQuery.Unity/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. - -[assembly: AssemblyTitle("LiveQuery.Unity")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("")] -[assembly: AssemblyCopyright("${AuthorCopyright}")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. - -[assembly: AssemblyVersion("1.0.*")] - -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. - -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] diff --git a/LiveQuery/Source/AVLiveQuery.cs b/LiveQuery/Source/AVLiveQuery.cs deleted file mode 100644 index b414d4d..0000000 --- a/LiveQuery/Source/AVLiveQuery.cs +++ /dev/null @@ -1,225 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using System.Collections; -using System.Collections.Generic; -using LeanCloud.Storage.Internal; -using LeanCloud.Realtime; -using LeanCloud.Realtime.Internal; -using System.Linq; -using System.Linq.Expressions; - -namespace LeanCloud.LiveQuery -{ - /// - /// AVLiveQuery 类 - /// - public static class AVLiveQuery - { - /// - /// LiveQuery 传输数据的 AVRealtime 实例 - /// - public static AVRealtime Channel { - get; set; - } - - internal static long ClientTs { - get; set; - } - - internal static bool Inited { - get; set; - } - - internal static string InstallationId { - get; set; - } - } - - /// - /// AVLiveQuery 对象 - /// - /// - public class AVLiveQuery where T : AVObject - { - internal static Dictionary>> liveQueryDict = new Dictionary>>(); - - - /// - /// 当前 AVLiveQuery 对象的 Id - /// - public string Id { get; set; } - - /// - /// 根据 AVQuery 创建 AVLiveQuery 对象 - /// - /// - public AVLiveQuery(AVQuery query) { - this.Query = query; - } - /// - /// AVLiveQuery 对应的 AVQuery 对象 - /// - public AVQuery Query { get; set; } - - /// - /// 数据推送的触发的事件通知 - /// - public event EventHandler> OnLiveQueryReceived; - - /// - /// 推送抵达时触发事件通知 - /// - /// 产生这条推送的原因。 - /// - /// create:符合查询条件的对象创建; - /// update:符合查询条件的对象属性修改。 - /// enter:对象修改事件,从不符合查询条件变成符合。 - /// leave:对象修改时间,从符合查询条件变成不符合。 - /// delete:对象删除 - /// login:只对 _User 对象有效,表示用户登录。 - /// - /// - /// - public void On(string scope, Action onRecevived) - { - this.OnLiveQueryReceived += (sender, e) => - { - if (e.Scope == scope) - { - onRecevived.Invoke(e.Payload); - } - }; - } - - /// - /// 订阅操作 - /// - /// - /// - public async Task> SubscribeAsync(CancellationToken cancellationToken = default(CancellationToken)) { - if (Query == null) { - throw new Exception("Query can not be null when subcribe."); - } - if (!AVLiveQuery.Inited) { - await Login(); - AVLiveQuery.Channel.OnReconnected += OnChannelReconnected; - AVLiveQuery.Channel.NoticeReceived += OnChannelNoticeReceived; - AVLiveQuery.Inited = true; - } - await InternalSubscribe(); - var liveQueryRef = new WeakReference>(this); - liveQueryDict.Add(Id, liveQueryRef); - return this; - } - - static async void OnChannelReconnected(object sender, AVIMReconnectedEventArgs e) { - await Login(); - lock (liveQueryDict) { - foreach (var kv in liveQueryDict) { - if (kv.Value.TryGetTarget(out var liveQuery)) { - liveQuery.InternalSubscribe().ContinueWith(_ => { }); - } - } - } - } - - static async Task Login() { - var installation = await AVPlugins.Instance.InstallationIdController.GetAsync(); - AVLiveQuery.InstallationId = installation.ToString(); - AVLiveQuery.Channel.ToggleNotification(true); - await AVLiveQuery.Channel.OpenAsync(); - AVLiveQuery.ClientTs = (long) DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds; - var liveQueryLogInCmd = new AVIMCommand().Command("login") - .Argument("installationId", AVLiveQuery.InstallationId) - .Argument("clientTs", AVLiveQuery.ClientTs) - .Argument("service", 1).AppId(AVClient.CurrentConfiguration.ApplicationId); - // open the session for LiveQuery. - try { - await AVLiveQuery.Channel.AVIMCommandRunner.RunCommandAsync(liveQueryLogInCmd); - } catch (Exception e) { - AVRealtime.PrintLog(e.Message); - } - } - - static void OnChannelNoticeReceived(object sender, AVIMNotice e) { - if (e.CommandName == "data") { - var ids = AVDecoder.Instance.DecodeList(e.RawData["ids"]); - if (e.RawData["msg"] is IEnumerable msg) { - var receivedPayloads = from item in msg - select item as Dictionary; - if (receivedPayloads != null) { - foreach (var payload in receivedPayloads) { - var liveQueryId = payload["query_id"] as string; - if (liveQueryDict.TryGetValue(liveQueryId, out var liveQueryRef) && - liveQueryRef.TryGetTarget(out var liveQuery)) { - var scope = payload["op"] as string; - var objectPayload = payload["object"] as Dictionary; - string[] keys = null; - if (payload.TryGetValue("updatedKeys", out object updatedKeys)) { - // enter, leave, update - keys = (updatedKeys as List).Select(x => x.ToString()).ToArray(); - } - liveQuery.Emit(scope, objectPayload, keys); - } - } - } - } - } - } - - async Task InternalSubscribe() { - var queryMap = new Dictionary { - { "where", Query.Condition}, - { "className", Query.GetClassName()} - }; - - Dictionary data = new Dictionary { - { "query", queryMap }, - { "id", AVLiveQuery.InstallationId }, - { "clientTimestamp", AVLiveQuery.ClientTs } - }; - string sessionToken = AVUser.CurrentUser?.SessionToken; - if (!string.IsNullOrEmpty(sessionToken)) { - data.Add("sessionToken", sessionToken); - } - var command = new AVCommand("LiveQuery/subscribe", - "POST", - sessionToken, - data: data); - var res = await AVPlugins.Instance.CommandRunner.RunCommandAsync(command); - Id = res.Item2["query_id"] as string; - } - - /// - /// 取消对当前 LiveQuery 对象的订阅 - /// - /// - public async Task UnsubscribeAsync() { - Dictionary strs = new Dictionary { - { "id", AVLiveQuery.InstallationId }, - { "query_id", Id }, - }; - string sessionToken = AVUser.CurrentUser?.SessionToken; - var command = new AVCommand("LiveQuery/unsubscribe", - "POST", - sessionToken, - data: strs); - await AVPlugins.Instance.CommandRunner.RunCommandAsync(command); - lock (liveQueryDict) { - liveQueryDict.Remove(Id); - } - } - - void Emit(string scope, IDictionary payloadMap, string[] keys) { - var objectState = AVObjectCoder.Instance.Decode(payloadMap, AVDecoder.Instance); - var payloadObject = AVObject.FromState(objectState, Query.GetClassName()); - var args = new AVLiveQueryEventArgs { - Scope = scope, - Keys = keys, - Payload = payloadObject - }; - OnLiveQueryReceived?.Invoke(this, args); - } - } -} diff --git a/LiveQuery/Source/AVLiveQueryEventArgs.cs b/LiveQuery/Source/AVLiveQueryEventArgs.cs deleted file mode 100644 index 98e860f..0000000 --- a/LiveQuery/Source/AVLiveQueryEventArgs.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Collections.Generic; -using LeanCloud; -using System.Collections; - -namespace LeanCloud.LiveQuery -{ - /// - /// AVLiveQuery 回调参数 - /// - public class AVLiveQueryEventArgs : EventArgs - where T : AVObject - { - internal AVLiveQueryEventArgs() - { - - } - - /// - /// 更新事件 - /// - public string Scope { get; set; } - - /// - /// 更新字段 - /// - public IEnumerable Keys { get; set; } - - /// - /// 更新数据 - /// - public T Payload { get; set; } - } -} diff --git a/LiveQuery/Source/AVLiveQueryExtensions.cs b/LiveQuery/Source/AVLiveQueryExtensions.cs deleted file mode 100644 index a15218c..0000000 --- a/LiveQuery/Source/AVLiveQueryExtensions.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud.LiveQuery -{ - /// - /// AVLiveQuery 扩展类 - /// - public static class AVLiveQueryExtensions - { - /// - /// AVQuery 订阅 AVLiveQuery 的扩展方法 - /// - /// AVLiveQuery 对象 - /// Query. - /// Cancellation token. - /// The 1st type parameter. - public static async Task> SubscribeAsync(this AVQuery query, CancellationToken cancellationToken = default(CancellationToken)) where T : AVObject { - var liveQuery = new AVLiveQuery(query); - liveQuery = await liveQuery.SubscribeAsync(); - return liveQuery; - } - } -} diff --git a/RTM/RTM.PCL/RTM.PCL.csproj b/RTM/RTM.PCL/RTM.PCL.csproj index 72b7e89..a67701e 100644 --- a/RTM/RTM.PCL/RTM.PCL.csproj +++ b/RTM/RTM.PCL/RTM.PCL.csproj @@ -206,11 +206,5 @@ ..\..\packages\Websockets.Pcl.1.1.9\lib\portable-net45+win+wpa81+wp80+MonoAndroid10+xamarinios10\WebSockets.PCL.dll - - - {659D19F0-9A40-42C0-886C-555E64F16848} - Storage.PCL - - \ No newline at end of file diff --git a/RTM/RTM.Unity/RTM.Unity.csproj b/RTM/RTM.Unity/RTM.Unity.csproj index 51ba4ed..2d18b38 100644 --- a/RTM/RTM.Unity/RTM.Unity.csproj +++ b/RTM/RTM.Unity/RTM.Unity.csproj @@ -215,11 +215,5 @@ Internal\WebSocket\Unity\websocket-sharp.dll - - - {A0D50BCB-E50E-4AAE-8E7D-24BF5AE33DAC} - Storage.Unity - - \ No newline at end of file diff --git a/RTM/RTM/Internal/AVIMCorePlugins.cs b/RTM/RTM/Internal/AVIMCorePlugins.cs deleted file mode 100644 index 859e0bc..0000000 --- a/RTM/RTM/Internal/AVIMCorePlugins.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud.Realtime.Internal -{ - internal class AVIMCorePlugins - { - private static readonly AVIMCorePlugins instance = new AVIMCorePlugins(); - public static AVIMCorePlugins Instance - { - get - { - return instance; - } - } - - private readonly object mutex = new object(); - - private IAVRouterController routerController; - public IAVRouterController RouterController - { - get - { - lock (mutex) - { - routerController = routerController ?? new AVRouterController(); - return routerController; - } - } - internal set - { - lock (mutex) - { - routerController = value; - } - } - } - - - private IFreeStyleMessageClassingController freeStyleClassingController; - public IFreeStyleMessageClassingController FreeStyleClassingController - { - get - { - lock (mutex) - { - freeStyleClassingController = freeStyleClassingController ?? new FreeStyleMessageClassingController(); - return freeStyleClassingController; - } - } - } - } -} diff --git a/RTM/RTM/Internal/Command/AVIMCommand.cs b/RTM/RTM/Internal/Command/AVIMCommand.cs deleted file mode 100644 index 18300e5..0000000 --- a/RTM/RTM/Internal/Command/AVIMCommand.cs +++ /dev/null @@ -1,176 +0,0 @@ -using LeanCloud; -using LeanCloud.Storage.Internal; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud.Realtime.Internal -{ - /// - /// Command. - /// - public class AVIMCommand - { - protected readonly string cmd; - protected readonly string op; - protected string appId; - protected string peerId; - protected AVIMSignature signature; - protected readonly IDictionary arguments; - - public int TimeoutInSeconds { get; set; } - - protected readonly IDictionary estimatedData = new Dictionary(); - internal readonly object mutex = new object(); - internal static readonly object Mutex = new object(); - - public AVIMCommand() : - this(arguments: new Dictionary()) - { - - } - protected AVIMCommand(string cmd = null, - string op = null, - string appId = null, - string peerId = null, - AVIMSignature signature = null, - IDictionary arguments = null) - { - this.cmd = cmd; - this.op = op; - this.arguments = arguments == null ? new Dictionary() : arguments; - this.peerId = peerId; - this.signature = signature; - } - - protected AVIMCommand(AVIMCommand source, - string cmd = null, - string op = null, - string appId = null, - string peerId = null, - IDictionary arguments = null, - AVIMSignature signature = null) - { - if (source == null) - { - throw new ArgumentNullException("source", "Source can not be null"); - } - this.cmd = source.cmd; - this.op = source.op; - this.arguments = source.arguments; - this.peerId = source.peerId; - this.appId = source.appId; - this.signature = source.signature; - - if (cmd != null) - { - this.cmd = cmd; - } - if (op != null) - { - this.op = op; - } - if (arguments != null) - { - this.arguments = arguments; - } - if (peerId != null) - { - this.peerId = peerId; - } - if (appId != null) - { - this.appId = appId; - } - if (signature != null) - { - this.signature = signature; - } - } - - public AVIMCommand Command(string cmd) - { - return new AVIMCommand(this, cmd: cmd); - } - public AVIMCommand Option(string op) - { - return new AVIMCommand(this, op: op); - } - public AVIMCommand Argument(string key, object value) - { - lock (mutex) - { - this.arguments[key] = value; - return new AVIMCommand(this); - } - } - public AVIMCommand AppId(string appId) - { - this.appId = appId; - return new AVIMCommand(this, appId: appId); - } - - public AVIMCommand PeerId(string peerId) - { - this.peerId = peerId; - return new AVIMCommand(this, peerId: peerId); - } - - public AVIMCommand IDlize() - { - this.Argument("i", AVIMCommand.NextCmdId); - return this; - } - - public virtual IDictionary Encode() - { - lock (mutex) - { - estimatedData.Clear(); - estimatedData.Merge(arguments); - estimatedData.Add("cmd", cmd); - estimatedData.Add("appId", this.appId); - if (!string.IsNullOrEmpty(op)) - estimatedData.Add("op", op); - if (!string.IsNullOrEmpty(peerId)) - estimatedData.Add("peerId", peerId); - - return estimatedData; - } - } - - public virtual string EncodeJsonString() - { - var json = this.Encode(); - return Json.Encode(json); - } - - public bool IsValid - { - get - { - return !string.IsNullOrEmpty(this.cmd); - } - } - - private static Int32 lastCmdId = -65536; - internal static Int32 NextCmdId - { - get - { - lock (Mutex) - { - lastCmdId++; - - if (lastCmdId > ushort.MaxValue) - { - lastCmdId = -65536; - } - return lastCmdId; - } - } - } - } -} diff --git a/RTM/RTM/Internal/Command/AVIMCommandRunner.cs b/RTM/RTM/Internal/Command/AVIMCommandRunner.cs deleted file mode 100644 index 2052c46..0000000 --- a/RTM/RTM/Internal/Command/AVIMCommandRunner.cs +++ /dev/null @@ -1,92 +0,0 @@ -using LeanCloud; -using LeanCloud.Storage.Internal; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud.Realtime.Internal -{ - public class AVIMCommandRunner : IAVIMCommandRunner - { - private readonly IWebSocketClient webSocketClient; - public AVIMCommandRunner(IWebSocketClient webSocketClient) - { - this.webSocketClient = webSocketClient; - } - - public void RunCommand(AVIMCommand command) - { - command.IDlize(); - var requestString = command.EncodeJsonString(); - AVRealtime.PrintLog("websocket=>" + requestString); - webSocketClient.Send(requestString); - } - - /// - /// - /// - /// - /// - /// - public Task>> RunCommandAsync(AVIMCommand command, CancellationToken cancellationToken = default(CancellationToken)) - { - var tcs = new TaskCompletionSource>>(); - - command.IDlize(); - - var requestString = command.EncodeJsonString(); - if (!command.IsValid) - { - requestString = "{}"; - } - AVRealtime.PrintLog("websocket=>" + requestString); - webSocketClient.Send(requestString); - var requestJson = command.Encode(); - - - Action onMessage = null; - onMessage = (response) => - { - //AVRealtime.PrintLog("response<=" + response); - var responseJson = Json.Parse(response) as IDictionary; - if (responseJson.Keys.Contains("i")) - { - if (requestJson["i"].ToString() == responseJson["i"].ToString()) - { - var result = new Tuple>(-1, responseJson); - if (responseJson.Keys.Contains("code")) - { - var errorCode = int.Parse(responseJson["code"].ToString()); - var reason = string.Empty; - int appCode = 0; - - if (responseJson.Keys.Contains("reason")) - { - reason = responseJson["reason"].ToString(); - } - if (responseJson.Keys.Contains("appCode")) - { - appCode = int.Parse(responseJson["appCode"].ToString()); - } - tcs.SetException(new AVIMException(errorCode, appCode, reason, null)); - } - if (tcs.Task.Exception == null) - { - tcs.SetResult(result); - } - webSocketClient.OnMessage -= onMessage; - } - else - { - - } - } - }; - webSocketClient.OnMessage += onMessage; - return tcs.Task; - } - } -} diff --git a/RTM/RTM/Internal/Command/AckCommand.cs b/RTM/RTM/Internal/Command/AckCommand.cs deleted file mode 100644 index 938f078..0000000 --- a/RTM/RTM/Internal/Command/AckCommand.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud.Realtime.Internal -{ - internal class AckCommand : AVIMCommand - { - public AckCommand() - : base(cmd: "ack") - { - - } - - public AckCommand(AVIMCommand source) - : base(source) - { - - } - - public AckCommand Message(IAVIMMessage message) - { - return new AckCommand() - .ConversationId(message.ConversationId) - .MessageId(message.Id); - } - - public AckCommand MessageId(string messageId) - { - if (string.IsNullOrEmpty(messageId)) - { - messageId = ""; - } - return new AckCommand(this.Argument("mid", messageId)); - } - - public AckCommand ConversationId(string conversationId) - { - if (string.IsNullOrEmpty(conversationId)) - { - conversationId = ""; - } - return new AckCommand(this.Argument("cid", conversationId)); - } - - public AckCommand FromTimeStamp(long startTimeStamp) - { - return new AckCommand(this.Argument("fromts", startTimeStamp)); - } - - public AckCommand ToTimeStamp(long endTimeStamp) - { - return new AckCommand(this.Argument("tots", endTimeStamp)); - } - - public AckCommand ReadAck() - { - return new AckCommand(this.Argument("read", true)); - } - } -} diff --git a/RTM/RTM/Internal/Command/ConversationCommand.cs b/RTM/RTM/Internal/Command/ConversationCommand.cs deleted file mode 100644 index ab4de2d..0000000 --- a/RTM/RTM/Internal/Command/ConversationCommand.cs +++ /dev/null @@ -1,129 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud.Realtime.Internal -{ - internal class ConversationCommand : AVIMCommand - { - protected IList members; - public ConversationCommand() - : base(cmd: "conv") - { - - } - - public ConversationCommand(AVIMCommand source) - : base(source: source) - { - } - - public ConversationCommand Member(string clientId) - { - if (members == null) - { - members = new List(); - } - members.Add(clientId); - return Members(members); - } - - public ConversationCommand Members(IEnumerable members) - { - this.members = members.ToList(); - return new ConversationCommand(this.Argument("m", members)); - } - - public ConversationCommand Transient(bool isTransient) - { - return new ConversationCommand(this.Argument("transient", isTransient)); - } - - public ConversationCommand Unique(bool isUnique) - { - return new ConversationCommand(this.Argument("unique", isUnique)); - } - - public ConversationCommand Temporary(bool isTemporary) - { - return new ConversationCommand(this.Argument("tempConv", isTemporary)); - } - - public ConversationCommand TempConvTTL(double tempConvTTL) - { - return new ConversationCommand(this.Argument("tempConvTTL", tempConvTTL)); - } - - public ConversationCommand Attr(IDictionary attr) - { - return new ConversationCommand(this.Argument("attr", attr)); - } - - public ConversationCommand Set(string key, object value) - { - return new ConversationCommand(this.Argument(key, value)); - } - - public ConversationCommand ConversationId(string conversationId) - { - return new ConversationCommand(this.Argument("cid", conversationId)); - } - - public ConversationCommand Generate(AVIMConversation conversation) - { - var attr = conversation.EncodeAttributes(); - var cmd = new ConversationCommand() - .ConversationId(conversation.ConversationId) - .Attr(attr) - .Members(conversation.MemberIds) - .Transient(conversation.IsTransient) - .Temporary(conversation.IsTemporary); - - if (conversation.IsTemporary) - { - var ttl = (conversation.expiredAt.Value - DateTime.Now).TotalSeconds; - cmd = cmd.TempConvTTL(ttl); - } - - return cmd; - } - - public ConversationCommand Where(object encodedQueryString) - { - return new ConversationCommand(this.Argument("where", encodedQueryString)); - } - - public ConversationCommand Limit(int limit) - { - return new ConversationCommand(this.Argument("limit", limit)); - } - - public ConversationCommand Skip(int skip) - { - return new ConversationCommand(this.Argument("skip", skip)); - } - - public ConversationCommand Count() - { - return new ConversationCommand(this.Argument("count", 1)); - } - - public ConversationCommand Sort(string sort) - { - return new ConversationCommand(this.Argument("sort", sort)); - } - - public ConversationCommand TargetClientId(string targetClientId) - { - return new ConversationCommand(this.Argument("targetClientId", targetClientId)); - } - - public ConversationCommand QueryAllMembers(bool queryAllMembers) - { - return new ConversationCommand(this.Argument("queryAllMembers", queryAllMembers)); - } - - } -} diff --git a/RTM/RTM/Internal/Command/IAVIMCommandRunner.cs b/RTM/RTM/Internal/Command/IAVIMCommandRunner.cs deleted file mode 100644 index a904f97..0000000 --- a/RTM/RTM/Internal/Command/IAVIMCommandRunner.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud.Realtime.Internal -{ - public interface IAVIMCommandRunner - { - Task>> RunCommandAsync(AVIMCommand command, - CancellationToken cancellationToken = default(CancellationToken)); - - void RunCommand(AVIMCommand command); - } -} diff --git a/RTM/RTM/Internal/Command/MessageCommand.cs b/RTM/RTM/Internal/Command/MessageCommand.cs deleted file mode 100644 index ec76d94..0000000 --- a/RTM/RTM/Internal/Command/MessageCommand.cs +++ /dev/null @@ -1,83 +0,0 @@ -using LeanCloud.Storage.Internal; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud.Realtime.Internal -{ - internal class MessageCommand : AVIMCommand - { - public MessageCommand() - : base(cmd: "direct") - { - - } - - public MessageCommand(AVIMCommand source) - : base(source: source) - { - - } - - public MessageCommand ConvId(string convId) - { - return new MessageCommand(this.Argument("cid", convId)); - } - - public MessageCommand Receipt(bool receipt) - { - return new MessageCommand(this.Argument("r", receipt)); - } - - public MessageCommand Transient(bool transient) - { - if (transient) return new MessageCommand(this.Argument("transient", transient)); - return new MessageCommand(this); - } - public MessageCommand Priority(int priority) - { - if (priority > 1) return new MessageCommand(this.Argument("level", priority)); - return new MessageCommand(this); - } - public MessageCommand Will(bool will) - { - if (will) return new MessageCommand(this.Argument("will", will)); - return new MessageCommand(this); - } - public MessageCommand Distinct(string token) - { - return new MessageCommand(this.Argument("dt", token)); - } - public MessageCommand Message(string msg) - { - return new MessageCommand(this.Argument("msg", msg)); - } - public MessageCommand BinaryEncode(bool binaryEncode) - { - return new MessageCommand(this.Argument("bin", binaryEncode)); - } - - public MessageCommand PushData(IDictionary pushData) - { - return new MessageCommand(this.Argument("pushData", Json.Encode(pushData))); - } - - public MessageCommand Mention(IEnumerable clientIds) - { - var mentionedMembers = clientIds.ToList(); - return new MessageCommand(this.Argument("mentionPids", mentionedMembers)); - } - - public MessageCommand MentionAll(bool mentionAll) - { - return new MessageCommand(this.Argument("mentionAll", mentionAll)); - } - - public MessageCommand Binary(byte[] data) - { - return new MessageCommand(this.Argument("binaryMsg", data)); - } - } -} diff --git a/RTM/RTM/Internal/Command/PatchCommand.cs b/RTM/RTM/Internal/Command/PatchCommand.cs deleted file mode 100644 index 113a0db..0000000 --- a/RTM/RTM/Internal/Command/PatchCommand.cs +++ /dev/null @@ -1,98 +0,0 @@ -using LeanCloud.Storage.Internal; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud.Realtime.Internal -{ - internal class PatchCommand : AVIMCommand - { - - internal struct Patch - { - public string MessageId { get; set; } - public string ConvId { get; set; } - public string From { get; set; } - public long MetaTimestamp { get; set; } - public long PatchTimestamp { get; set; } - public string PatchData { get; set; } - public bool Recall { get; set; } - public byte[] BinaryData { get; set; } - public bool MentionAll { get; set; } - public IEnumerable MentionIds { get; set; } - - public IDictionary Encode() - { - return new Dictionary() - { - { "cid",this.ConvId}, - { "mid",this.MessageId}, - { "from",this.From}, - { "timestamp",this.MetaTimestamp}, - { "recall",this.Recall}, - { "data",this.PatchData}, - { "patchTimestamp",this.PatchTimestamp}, - { "binaryMsg",this.BinaryData}, - { "mentionAll",this.MentionAll}, - { "meintonPids",this.MentionIds} - } as IDictionary; - } - } - - public PatchCommand() - : base(cmd: "patch", op: "modify") - { - this.Patches = new List(); - } - - public PatchCommand(AVIMCommand source, ICollection sourcePatchs) - : base(source: source) - { - this.Patches = sourcePatchs; - } - - public ICollection Patches { get; set; } - - public IList> EncodePatches() - { - return this.Patches.Select(p => p.Encode().Trim()).ToList(); - } - - public PatchCommand Recall(IAVIMMessage message) - { - var patch = new Patch() - { - ConvId = message.ConversationId, - From = message.FromClientId, - MessageId = message.Id, - MetaTimestamp = message.ServerTimestamp, - Recall = true, - PatchTimestamp = DateTime.Now.ToUnixTimeStamp() - }; - - this.Patches.Add(patch); - this.Argument("patches", this.EncodePatches()); - return new PatchCommand(this, this.Patches); - } - - public PatchCommand Modify(IAVIMMessage oldMessage, IAVIMMessage newMessage) - { - var patch = new Patch() - { - ConvId = oldMessage.ConversationId, - From = oldMessage.FromClientId, - MessageId = oldMessage.Id, - MetaTimestamp = oldMessage.ServerTimestamp, - Recall = false, - PatchTimestamp = DateTime.Now.ToUnixTimeStamp(), - PatchData = newMessage.Serialize() - }; - - this.Patches.Add(patch); - this.Argument("patches", this.EncodePatches()); - return new PatchCommand(this, this.Patches); - } - } -} diff --git a/RTM/RTM/Internal/Command/ReadCommand.cs b/RTM/RTM/Internal/Command/ReadCommand.cs deleted file mode 100644 index a627d00..0000000 --- a/RTM/RTM/Internal/Command/ReadCommand.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud.Realtime.Internal -{ - internal class ReadCommand : AVIMCommand - { - internal class ConvRead - { - internal string ConvId { get; set; } - internal string MessageId { get; set; } - internal long Timestamp { get; set; } - public override bool Equals(object obj) - { - ConvRead cr = obj as ConvRead; - return cr.ConvId == this.ConvId; - } - public override int GetHashCode() - { - return this.ConvId.GetHashCode() ^ this.MessageId.GetHashCode() ^ this.Timestamp.GetHashCode(); - } - } - - public ReadCommand() - : base(cmd: "read") - { - - } - - public ReadCommand(AVIMCommand source) - : base(source) - { - - } - - public ReadCommand ConvId(string convId) - { - return new ReadCommand(this.Argument("cid", convId)); - } - - public ReadCommand ConvIds(IEnumerable convIds) - { - if (convIds != null) - { - if (convIds.Count() > 0) - { - return new ReadCommand(this.Argument("cids", convIds.ToList())); - } - } - return this; - - } - - public ReadCommand Conv(ConvRead conv) - { - return Convs(new ConvRead[] { conv }); - } - - public ReadCommand Convs(IEnumerable convReads) - { - if (convReads != null) - { - if (convReads.Count() > 0) - { - IList> payload = new List>(); - - foreach (var convRead in convReads) - { - var convDic = new Dictionary(); - convDic.Add("cid", convRead.ConvId); - if (!string.IsNullOrEmpty(convRead.MessageId)) - { - convDic.Add("mid", convRead.MessageId); - } - if (convRead.Timestamp != 0) - { - convDic.Add("timestamp", convRead.Timestamp); - } - payload.Add(convDic); - } - - return new ReadCommand(this.Argument("convs", payload)); - } - } - return this; - } - } -} diff --git a/RTM/RTM/Internal/Command/SessionCommand.cs b/RTM/RTM/Internal/Command/SessionCommand.cs deleted file mode 100644 index f18b7b9..0000000 --- a/RTM/RTM/Internal/Command/SessionCommand.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud.Realtime.Internal -{ - internal class SessionCommand : AVIMCommand - { - static readonly int MESSAGE_RECALL_AND_MODIFY = 0x1; - - public SessionCommand() - : base(cmd: "session") - { - arguments.Add("configBitmap", MESSAGE_RECALL_AND_MODIFY); - } - - public SessionCommand(AVIMCommand source) - :base(source: source) - { - - } - - public SessionCommand UA(string ua) - { - return new SessionCommand(this.Argument("ua", ua)); - } - - public SessionCommand Tag(string tag) - { - if (string.IsNullOrEmpty(tag)) return new SessionCommand(this); - return new SessionCommand(this.Argument("tag", tag)); - } - - public SessionCommand DeviceId(string deviceId) - { - if (string.IsNullOrEmpty(deviceId)) return new SessionCommand(this); - return new SessionCommand(this.Argument("deviceId", deviceId)); - } - - public SessionCommand R(int r) - { - return new SessionCommand(this.Argument("r", r)); - } - - public SessionCommand SessionToken(string st) - { - return new SessionCommand(this.Argument("st", st)); - } - - public SessionCommand SessionPeerIds(IEnumerable sessionPeerIds) - { - return new SessionCommand(this.Argument("sessionPeerIds", sessionPeerIds.ToList())); - } - } -} diff --git a/RTM/RTM/Internal/DataEngine/Controller/DateTimeEngine.cs b/RTM/RTM/Internal/DataEngine/Controller/DateTimeEngine.cs deleted file mode 100644 index 25c920a..0000000 --- a/RTM/RTM/Internal/DataEngine/Controller/DateTimeEngine.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud.Realtime.Internal -{ - internal enum UnixTimeStampUnit - { - Second = 1, - Milisecond = 1000, - } - internal static class DateTimeEngine - { - public static long ToUnixTimeStamp(this DateTime date, UnixTimeStampUnit unit = UnixTimeStampUnit.Milisecond) - { - long unixTimestamp = (long)(date.ToUniversalTime().Subtract(new DateTime(1970, 1, 1))).TotalSeconds; - return (unixTimestamp * (int)unit); - } - - public static DateTime ToDateTime(this long timestamp, UnixTimeStampUnit unit = UnixTimeStampUnit.Milisecond) - { - var timespan = timestamp * 1000 / (int)(unit); - DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); - dtDateTime = dtDateTime.AddMilliseconds(timespan).ToLocalTime(); - return dtDateTime; - } - } -} diff --git a/RTM/RTM/Internal/DataEngine/Controller/DictionaryEngine.cs b/RTM/RTM/Internal/DataEngine/Controller/DictionaryEngine.cs deleted file mode 100644 index a1da600..0000000 --- a/RTM/RTM/Internal/DataEngine/Controller/DictionaryEngine.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud.Realtime.Internal -{ - internal static class DictionaryEngine - { - internal static IDictionary Merge(this IDictionary dataLeft, IDictionary dataRight) - { - if (dataRight == null) - return dataLeft; - foreach (var kv in dataRight) - { - if (dataLeft.ContainsKey(kv.Key)) - { - dataLeft[kv.Key] = kv.Value; - } - else - { - dataLeft.Add(kv); - } - } - return dataLeft; - } - - internal static object Grab(this IDictionary data, string path) - { - var keys = path.Split('.').ToList(); - if (keys.Count == 1) return data[keys[0]]; - - var deep = data[keys[0]] as IDictionary; - - keys.RemoveAt(0); - string deepPath = string.Join(".", keys.ToArray()); - - return Grab(deep, deepPath); - } - - internal static IDictionary Trim(this IDictionary data) - { - return data.Where(kvp => kvp.Value != null).ToDictionary(k => k.Key, v => v.Value); - } - } -} diff --git a/RTM/RTM/Internal/DataEngine/Controller/StringEngine.cs b/RTM/RTM/Internal/DataEngine/Controller/StringEngine.cs deleted file mode 100644 index 62e902a..0000000 --- a/RTM/RTM/Internal/DataEngine/Controller/StringEngine.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Linq; -using System.Collections.Generic; -using System.Linq.Expressions; -using System.Text; - -namespace LeanCloud.Realtime.Internal -{ - internal static class StringEngine - { - internal static string Random(this string str, int length) - { - const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"; - var random = new Random(); - return new string(Enumerable.Repeat(chars, length) - .Select(s => s[random.Next(s.Length)]).ToArray()); - } - - internal static string TempConvId(this IEnumerable objs) - { - var orderedBase64Strs = objs.Select(obj => Encoding.UTF8.ToBase64(obj.ToString())).OrderBy(a => a, StringComparer.Ordinal).ToArray(); - return "_tmp:" + string.Join("$", orderedBase64Strs); - } - - internal static string ToBase64(this System.Text.Encoding encoding, string text) - { - if (text == null) - { - return null; - } - - byte[] textAsBytes = encoding.GetBytes(text); - return Convert.ToBase64String(textAsBytes); - } - } -} diff --git a/RTM/RTM/Internal/IAVIMPlatformHooks.cs b/RTM/RTM/Internal/IAVIMPlatformHooks.cs deleted file mode 100644 index 53d737c..0000000 --- a/RTM/RTM/Internal/IAVIMPlatformHooks.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud.Realtime.Internal -{ - interface IAVIMPlatformHooks - { - IWebSocketClient WebSocketClient { get; } - - string ua { get; } - } -} diff --git a/RTM/RTM/Internal/Message/Subclassing/FreeStyleMessageClassInfo.cs b/RTM/RTM/Internal/Message/Subclassing/FreeStyleMessageClassInfo.cs deleted file mode 100644 index e1a1e56..0000000 --- a/RTM/RTM/Internal/Message/Subclassing/FreeStyleMessageClassInfo.cs +++ /dev/null @@ -1,75 +0,0 @@ -using LeanCloud.Storage.Internal; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; - -namespace LeanCloud.Realtime.Internal -{ - internal class FreeStyleMessageClassInfo - { - public TypeInfo TypeInfo { get; private set; } - public IDictionary PropertyMappings { get; private set; } - private ConstructorInfo Constructor { get; set; } - //private MethodInfo ValidateMethod { get; set; } - - public int TypeInt { get; set; } - - public FreeStyleMessageClassInfo(Type type, ConstructorInfo constructor) - { - TypeInfo = type.GetTypeInfo(); - Constructor = constructor; - PropertyMappings = ReflectionHelpers.GetProperties(type) - .Select(prop => Tuple.Create(prop, prop.GetCustomAttribute(true))) - .Where(t => t.Item2 != null) - .Select(t => Tuple.Create(t.Item1, t.Item2.FieldName)) - .ToDictionary(t => t.Item1.Name, t => t.Item2); - } - public bool Validate(string msgStr) - { - var instance = Instantiate(msgStr); - if (instance is AVIMTypedMessage) - { - try - { - var msgDic = Json.Parse(msgStr) as IDictionary; - if (msgDic != null) - { - if (msgDic.ContainsKey(AVIMProtocol.LCTYPE)) - { - return msgDic[AVIMProtocol.LCTYPE].ToString() == TypeInt.ToString(); - } - } - } - catch (Exception ex) - { - if (ex is ArgumentException) - { - return instance.Validate(msgStr); - } - } - - } - return instance.Validate(msgStr); - } - - public IAVIMMessage Instantiate(string msgStr) - { - var rtn = (IAVIMMessage)Constructor.Invoke(null); - return rtn; - } - - public static string GetMessageClassName(TypeInfo type) - { - var attribute = type.GetCustomAttribute(); - return attribute != null ? attribute.ClassName : null; - } - - public static int GetTypedInteger(TypeInfo type) - { - var attribute = type.GetCustomAttribute(); - return attribute != null ? attribute.TypeInteger : 0; - } - } -} diff --git a/RTM/RTM/Internal/Message/Subclassing/FreeStyleMessageClassingController.cs b/RTM/RTM/Internal/Message/Subclassing/FreeStyleMessageClassingController.cs deleted file mode 100644 index 04bd105..0000000 --- a/RTM/RTM/Internal/Message/Subclassing/FreeStyleMessageClassingController.cs +++ /dev/null @@ -1,210 +0,0 @@ -using LeanCloud.Storage.Internal; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading; - -namespace LeanCloud.Realtime.Internal -{ - internal class FreeStyleMessageClassingController : IFreeStyleMessageClassingController - { - private static readonly string messageClassName = "_AVIMMessage"; - private readonly IDictionary registeredInterfaces; - private readonly ReaderWriterLockSlim mutex; - - public FreeStyleMessageClassingController() - { - mutex = new ReaderWriterLockSlim(); - registeredInterfaces = new Dictionary(); - } - - public Type GetType(IDictionary msg) - { - throw new NotImplementedException(); - } - - public IAVIMMessage Instantiate(string msgStr, IDictionary buildInData) - { - FreeStyleMessageClassInfo info = null; - mutex.EnterReadLock(); - bool bin = false; - if (buildInData.ContainsKey("bin")) - { - bool.TryParse(buildInData["bin"].ToString(), out bin); - } - - if (bin) - { - var binMessage = new AVIMBinaryMessage(); - this.DecodeProperties(binMessage, buildInData); - return binMessage; - } - - var reverse = registeredInterfaces.Values.Reverse(); - foreach (var subInterface in reverse) - { - if (subInterface.Validate(msgStr)) - { - info = subInterface; - break; - } - } - - mutex.ExitReadLock(); - - var message = info != null ? info.Instantiate(msgStr) : new AVIMMessage(); - - this.DecodeProperties(message, buildInData); - - message.Deserialize(msgStr); - - return message; - } - - public IAVIMMessage DecodeProperties(IAVIMMessage message, IDictionary buildInData) - { - long timestamp; - if (buildInData.ContainsKey("timestamp")) - { - if (long.TryParse(buildInData["timestamp"].ToString(), out timestamp)) - { - message.ServerTimestamp = timestamp; - } - } - long ackAt; - if (buildInData.ContainsKey("ackAt")) - { - if (long.TryParse(buildInData["ackAt"].ToString(), out ackAt)) - { - message.RcpTimestamp = ackAt; - } - } - - if (buildInData.ContainsKey("from")) - { - message.FromClientId = buildInData["from"].ToString(); - } - if (buildInData.ContainsKey("msgId")) - { - message.Id = buildInData["msgId"].ToString(); - } - if (buildInData.ContainsKey("cid")) - { - message.ConversationId = buildInData["cid"].ToString(); - } - if (buildInData.ContainsKey("fromPeerId")) - { - message.FromClientId = buildInData["fromPeerId"].ToString(); - } - if (buildInData.ContainsKey("id")) - { - message.Id = buildInData["id"].ToString(); - } - if (buildInData.ContainsKey("mid")) - { - message.Id = buildInData["mid"].ToString(); - } - if (buildInData.ContainsKey("mentionPids")) - { - message.MentionList = AVDecoder.Instance.DecodeList(buildInData["mentionPids"]); - } - if (buildInData.TryGetValue("patchTimestamp", out object patchTimestampObj)) { - if (long.TryParse(patchTimestampObj.ToString(), out long patchTimestamp)) { - message.UpdatedAt = patchTimestamp; - } - } - - bool mentionAll; - if (buildInData.ContainsKey("mentionAll")) - { - if (bool.TryParse(buildInData["mentionAll"].ToString(), out mentionAll)) - { - message.MentionAll = mentionAll; - } - } - return message; - } - - public IDictionary EncodeProperties(IAVIMMessage subclass) - { - var type = subclass.GetType(); - var result = new Dictionary(); - var className = GetClassName(type); - var typeInt = GetTypeInt(type); - var propertMappings = GetPropertyMappings(className); - foreach (var propertyPair in propertMappings) - { - var propertyInfo = ReflectionHelpers.GetProperty(type, propertyPair.Key); - var operation = propertyInfo.GetValue(subclass, null); - if (operation != null) - result[propertyPair.Value] = PointerOrLocalIdEncoder.Instance.Encode(operation); - } - if (typeInt != 0) - { - result[AVIMProtocol.LCTYPE] = typeInt; - } - return result; - } - - public bool IsTypeValid(IDictionary msg, Type type) - { - return true; - } - - public void RegisterSubclass(Type type) - { - TypeInfo typeInfo = type.GetTypeInfo(); - - if (!typeof(IAVIMMessage).GetTypeInfo().IsAssignableFrom(typeInfo)) - { - throw new ArgumentException("Cannot register a type that is not a implementation of IAVIMMessage"); - } - var className = GetClassName(type); - var typeInt = GetTypeInt(type); - try - { - mutex.EnterWriteLock(); - ConstructorInfo constructor = type.FindConstructor(); - if (constructor == null) - { - throw new ArgumentException("Cannot register a type that does not implement the default constructor!"); - } - var classInfo = new FreeStyleMessageClassInfo(type, constructor); - if (typeInt != 0) - { - classInfo.TypeInt = typeInt; - } - registeredInterfaces[className] = classInfo; - } - finally - { - mutex.ExitWriteLock(); - } - } - public String GetClassName(Type type) - { - return type == typeof(IAVIMMessage) - ? messageClassName - : FreeStyleMessageClassInfo.GetMessageClassName(type.GetTypeInfo()); - } - public int GetTypeInt(Type type) - { - return type == typeof(AVIMTypedMessage) ? 0 : FreeStyleMessageClassInfo.GetTypedInteger(type.GetTypeInfo()); - } - public IDictionary GetPropertyMappings(String className) - { - FreeStyleMessageClassInfo info = null; - mutex.EnterReadLock(); - registeredInterfaces.TryGetValue(className, out info); - if (info == null) - { - registeredInterfaces.TryGetValue(messageClassName, out info); - } - mutex.ExitReadLock(); - - return info.PropertyMappings; - } - } -} diff --git a/RTM/RTM/Internal/Message/Subclassing/IFreeStyleMessageClassingController.cs b/RTM/RTM/Internal/Message/Subclassing/IFreeStyleMessageClassingController.cs deleted file mode 100644 index 0fc3912..0000000 --- a/RTM/RTM/Internal/Message/Subclassing/IFreeStyleMessageClassingController.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace LeanCloud.Realtime.Internal -{ - interface IFreeStyleMessageClassingController - { - bool IsTypeValid(IDictionary msg, Type type); - void RegisterSubclass(Type t); - IAVIMMessage Instantiate(string msgStr,IDictionary buildInData); - IDictionary EncodeProperties(IAVIMMessage subclass); - Type GetType(IDictionary msg); - String GetClassName(Type type); - IDictionary GetPropertyMappings(String className); - } -} diff --git a/RTM/RTM/Internal/Protocol/AVIMProtocol.cs b/RTM/RTM/Internal/Protocol/AVIMProtocol.cs deleted file mode 100644 index 6c71044..0000000 --- a/RTM/RTM/Internal/Protocol/AVIMProtocol.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud.Realtime.Internal -{ - internal class AVIMProtocol - { - #region msg format - static internal readonly string LCTYPE = "_lctype"; - static internal readonly string LCFILE = "_lcfile"; - static internal readonly string LCTEXT = "_lctext"; - static internal readonly string LCATTRS = "_lcattrs"; - static internal readonly string LCLOC = "_lcloc"; - #endregion - } -} diff --git a/RTM/RTM/Internal/Router/AVRouterController.cs b/RTM/RTM/Internal/Router/AVRouterController.cs deleted file mode 100644 index dc47848..0000000 --- a/RTM/RTM/Internal/Router/AVRouterController.cs +++ /dev/null @@ -1,173 +0,0 @@ -using LeanCloud.Storage.Internal; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud.Realtime.Internal -{ - internal class AVRouterController : IAVRouterController - { - const string routerUrl = "http://router.g0.push.leancloud.cn/v1/route?appId={0}"; - const string routerKey = "LeanCloud_RouterState"; - public Task GetAsync(string pushRouter = null, bool secure = true, CancellationToken cancellationToken = default(CancellationToken)) - { - //return Task.FromResult(new PushRouterState() - //{ - // server = "wss://rtm57.leancloud.cn/" - //}); - return LoadAysnc(cancellationToken).OnSuccess(_ => - { - var cache = _.Result; - var task = Task.FromResult(cache); - - if (cache == null || cache.expire < DateTime.Now.ToUnixTimeStamp()) - { - task = QueryAsync(pushRouter, secure, cancellationToken); - } - - return task; - }).Unwrap(); - } - - /// - /// 清理地址缓存 - /// - /// The cache. - public Task ClearCache() { - var tcs = new TaskCompletionSource(); - AVPlugins.Instance.StorageController.LoadAsync().ContinueWith(t => { - if (t.IsFaulted) { - tcs.SetResult(true); - } else { - var storage = t.Result; - if (storage.ContainsKey(routerKey)) { - storage.RemoveAsync(routerKey).ContinueWith(_ => tcs.SetResult(true)); - } else { - tcs.SetResult(true); - } - } - }); - return tcs.Task; - } - - Task LoadAysnc(CancellationToken cancellationToken) - { - try - { - return AVPlugins.Instance.StorageController.LoadAsync().OnSuccess(_ => - { - var currentCache = _.Result; - object routeCacheStr = null; - if (currentCache.TryGetValue(routerKey, out routeCacheStr)) - { - var routeCache = routeCacheStr as IDictionary; - var routerState = new PushRouterState() - { - groupId = routeCache["groupId"] as string, - server = routeCache["server"] as string, - secondary = routeCache["secondary"] as string, - ttl = long.Parse(routeCache["ttl"].ToString()), - expire = long.Parse(routeCache["expire"].ToString()), - source = "localCache" - }; - return routerState; - } - return null; - }); - } - catch - { - return Task.FromResult(null); - } - } - - Task QueryAsync(string pushRouter, bool secure, CancellationToken cancellationToken) - { - var routerHost = pushRouter; - if (routerHost == null) { - var appRouter = AVPlugins.Instance.AppRouterController.Get(); - routerHost = string.Format("https://{0}/v1/route?appId={1}", appRouter.RealtimeRouterServer, AVClient.CurrentConfiguration.ApplicationId) ?? appRouter.RealtimeRouterServer ?? string.Format(routerUrl, AVClient.CurrentConfiguration.ApplicationId); - } - AVRealtime.PrintLog($"router: {routerHost}"); - AVRealtime.PrintLog($"push: {pushRouter}"); - if (!string.IsNullOrEmpty(pushRouter)) - { - var rtmUri = new Uri(pushRouter); - if (!string.IsNullOrEmpty(rtmUri.Scheme)) - { - var url = new Uri(rtmUri, "v1/route").ToString(); - routerHost = string.Format("{0}?appId={1}", url, AVClient.CurrentConfiguration.ApplicationId); - } - else - { - routerHost = string.Format("https://{0}/v1/route?appId={1}", pushRouter, AVClient.CurrentConfiguration.ApplicationId); - } - } - if (secure) - { - routerHost += "&secure=1"; - } - - AVRealtime.PrintLog("use push router url:" + routerHost); - - return AVClient.RequestAsync(uri: new Uri(routerHost), - method: "GET", - headers: null, - data: null, - contentType: "application/json", - cancellationToken: CancellationToken.None).ContinueWith(t => - { - if (t.Exception != null) - { - var innnerException = t.Exception.InnerException; - AVRealtime.PrintLog(innnerException.Message); - throw innnerException; - } - var httpStatus = (int)t.Result.Item1; - if (httpStatus != 200) - { - return null; - } - try - { - var result = t.Result.Item2; - - var routerState = Json.Parse(result) as IDictionary; - if (routerState.Keys.Count == 0) - { - throw new KeyNotFoundException("Can not get websocket url from server,please check the appId."); - } - var ttl = long.Parse(routerState["ttl"].ToString()); - var expire = DateTime.Now.AddSeconds(ttl); - routerState["expire"] = expire.ToUnixTimeStamp(); - - //save to local cache async. - AVPlugins.Instance.StorageController.LoadAsync().OnSuccess(storage => storage.Result.AddAsync(routerKey, routerState)); - var routerStateObj = new PushRouterState() - { - groupId = routerState["groupId"] as string, - server = routerState["server"] as string, - secondary = routerState["secondary"] as string, - ttl = long.Parse(routerState["ttl"].ToString()), - expire = expire.ToUnixTimeStamp(), - source = "online" - }; - - return routerStateObj; - } - catch (Exception e) - { - if (e is KeyNotFoundException) - { - throw e; - } - return null; - } - - }); - } - } -} diff --git a/RTM/RTM/Internal/Router/IAVRouterController.cs b/RTM/RTM/Internal/Router/IAVRouterController.cs deleted file mode 100644 index b7eebfb..0000000 --- a/RTM/RTM/Internal/Router/IAVRouterController.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud.Realtime.Internal -{ - interface IAVRouterController - { - Task GetAsync(string pushRouter, bool secure, CancellationToken cancellationToken = default(CancellationToken)); - Task ClearCache(); - } -} diff --git a/RTM/RTM/Internal/Router/State/RouterState.cs b/RTM/RTM/Internal/Router/State/RouterState.cs deleted file mode 100644 index 66eaea9..0000000 --- a/RTM/RTM/Internal/Router/State/RouterState.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud.Realtime.Internal -{ - internal class PushRouterState - { - public string groupId { get; internal set; } - public string server { get; internal set; } - public long ttl { get; internal set; } - public long expire { get; internal set; } - public string secondary { get; internal set; } - public string groupUrl { get; internal set; } - - public string source { get; internal set; } - } -} diff --git a/RTM/RTM/Internal/Timer/IAVTimer.cs b/RTM/RTM/Internal/Timer/IAVTimer.cs deleted file mode 100644 index 5573041..0000000 --- a/RTM/RTM/Internal/Timer/IAVTimer.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -namespace LeanCloud.Realtime.Internal -{ - public interface IAVTimer - { - /// - /// Start this timer. - /// - void Start(); - - /// - /// Stop this timer. - /// - void Stop(); - - bool Enabled { get; set; } - - /// - /// The number of milliseconds between timer events. - /// - /// The interval. - double Interval { get; set; } - - /// - /// 已经执行了多少次 - /// - long Executed { get; } - - /// - /// Occurs when elapsed. - /// - event EventHandler Elapsed; - } - /// - /// Timer event arguments. - /// - public class TimerEventArgs : EventArgs - { - public TimerEventArgs(DateTime signalTime) - { - SignalTime = signalTime; - } - public DateTime SignalTime - { - get; - private set; - } - } -} diff --git a/RTM/RTM/Internal/Timer/Portable/AVTimer.Portable.cs b/RTM/RTM/Internal/Timer/Portable/AVTimer.Portable.cs deleted file mode 100644 index e981dbd..0000000 --- a/RTM/RTM/Internal/Timer/Portable/AVTimer.Portable.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; -using System.Threading.Tasks; -using System.Threading; - -namespace LeanCloud.Realtime.Internal -{ - internal delegate void TimerCallback(); - - internal sealed class Timer : CancellationTokenSource, IDisposable - { - TimerCallback exe; - int Interval { get; set; } - - internal Timer(TimerCallback callback, int interval, bool enable) - { - exe = callback; - Interval = interval; - - Enabled = enable; - Execute(); - } - - Task Execute() - { - if (Enabled) - return Task.Delay(Interval).ContinueWith(t => - { - if (!Enabled) - return null; - exe(); - return this.Execute(); - }); - else - return Task.FromResult(0); - } - - volatile bool enabled; - public bool Enabled - { - get { - return enabled; - } set { - enabled = value; - } - } - } - - public class AVTimer : IAVTimer - { - public AVTimer() - { - - } - - Timer timer; - - public bool Enabled - { - get - { - return timer.Enabled; - } - set - { - timer.Enabled = value; - } - } - - public double Interval - { - get; set; - } - - long executed; - - public long Executed - { - get - { - return executed; - } - - internal set - { - executed = value; - } - } - - public void Start() - { - if (timer == null) - { - timer = new Timer(() => - { - Elapsed(this, new TimerEventArgs(DateTime.Now)); - }, (int)Interval, true); - } - } - - public void Stop() - { - if (timer != null) timer.Enabled = false; - } - - public event EventHandler Elapsed; - } -} diff --git a/RTM/RTM/Internal/WebSocket/IWebSocketClient.cs b/RTM/RTM/Internal/WebSocket/IWebSocketClient.cs deleted file mode 100644 index 6e670d2..0000000 --- a/RTM/RTM/Internal/WebSocket/IWebSocketClient.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud.Realtime.Internal -{ - /// - /// LeanCloud WebSocket 客户端接口 - /// - public interface IWebSocketClient { - /// - /// 客户端 WebSocket 长连接是否打开 - /// - bool IsOpen { get; } - - /// - /// WebSocket 长连接关闭时触发的事件回调 - /// - event Action OnClosed; - - /// - /// 云端发送数据包给客户端,WebSocket 接受到时触发的事件回调 - /// - event Action OnMessage; - - /// - /// 客户端 WebSocket 长连接成功打开时,触发的事件回调 - /// - event Action OnOpened; - - /// - /// 主动关闭连接 - /// - void Close(); - - void Disconnect(); - - /// - /// 打开连接 - /// - /// wss 地址 - /// 子协议 - void Open(string url, string protocol = null); - /// - /// 发送数据包的接口 - /// - /// - void Send(string message); - - Task Connect(string url, string protocol = null); - } -} diff --git a/RTM/RTM/Internal/WebSocket/Portable/DefaultWebSocketClient.Portable.cs b/RTM/RTM/Internal/WebSocket/Portable/DefaultWebSocketClient.Portable.cs deleted file mode 100644 index 54ee3f6..0000000 --- a/RTM/RTM/Internal/WebSocket/Portable/DefaultWebSocketClient.Portable.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Net.WebSockets; - -namespace LeanCloud.Realtime.Internal { - /// - /// LeanCloud Realtime SDK for .NET Portable 内置默认的 WebSocketClient - /// - public class DefaultWebSocketClient { - const int RECV_BUFFER_SIZE = 1024; - - ClientWebSocket client; - - public event Action OnClose; - public event Action OnOpened; - public event Action OnMessage; - - public bool IsOpen { - get { - return client != null && client.State == WebSocketState.Open; - } - } - - public async Task Close() { - if (IsOpen) { - await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "1", CancellationToken.None); - } - } - - public void Disconnect() { - OnClose?.Invoke(0, string.Empty); - _ = Close(); - } - - public async Task Send(string message) { - if (!IsOpen) { - throw new Exception("WebSocket is not open when send data."); - } - ArraySegment bytes = new ArraySegment(Encoding.UTF8.GetBytes(message)); - try { - await client.SendAsync(bytes, WebSocketMessageType.Text, true, default); - } catch (InvalidOperationException e) { - OnClose?.Invoke(-2, e.Message); - _ = Close(); - throw e; - } - } - - public async Task Connect(string url, string protocol = null) { - client = new ClientWebSocket(); - client.Options.AddSubProtocol(protocol); - client.Options.KeepAliveInterval = TimeSpan.FromSeconds(10); - try { - await client.ConnectAsync(new Uri(url), default); - // 开始接收 - _ = StartReceive(); - } catch (Exception e) { - - throw e; - } - } - - async Task StartReceive() { - byte[] buffer = new byte[RECV_BUFFER_SIZE]; - try { - while (client.State == WebSocketState.Open) { - byte[] data = new byte[0]; - WebSocketReceiveResult result; - do { - result = await client.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - if (result.MessageType == WebSocketMessageType.Close) { - // 断开事件 - AVRealtime.PrintLog($"-------------------- WebSocket Close: {result.CloseStatus}, {result.CloseStatusDescription}"); - OnClose?.Invoke((int)result.CloseStatus, result.CloseStatusDescription); - return; - } - data = await MergeData(data, buffer, result.Count); - } while (!result.EndOfMessage); - // 一个 WebSocket 消息体接收完成 - try { - string message = Encoding.UTF8.GetString(data); - OnMessage(message); - } catch (Exception e) { - AVRealtime.PrintLog($"************************* Parse command error: {e.Message}"); - } - } - } catch (Exception e) { - AVRealtime.PrintLog($"-------------------- WebSocket Receive Exception: {e.Message}"); - AVRealtime.PrintLog(e.StackTrace); - // 断线事件 - OnClose?.Invoke(-1, e.Message); - } - } - - static async Task MergeData(byte[] oldData, byte[] newData, int newDataLength) { - return await Task.Run(() => { - var data = new byte[oldData.Length + newDataLength]; - Array.Copy(oldData, data, oldData.Length); - Array.Copy(newData, 0, data, oldData.Length, newDataLength); - AVRealtime.PrintLog($"merge: {data.Length}"); - return data; - }); - } - } -} diff --git a/RTM/RTM/Public/AVIMAudioMessage.cs b/RTM/RTM/Public/AVIMAudioMessage.cs deleted file mode 100644 index 3c0907e..0000000 --- a/RTM/RTM/Public/AVIMAudioMessage.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud.Realtime -{ - /// - /// Audio message. - /// - [AVIMMessageClassName("_AVIMAudioMessage")] - [AVIMTypedMessageTypeInt(-3)] - public class AVIMAudioMessage : AVIMFileMessage - { - - } - - /// - /// Video message. - /// - [AVIMMessageClassName("_AVIMVideoMessage")] - [AVIMTypedMessageTypeInt(-4)] - public class AVIMVideoMessage: AVIMFileMessage - { - - } -} diff --git a/RTM/RTM/Public/AVIMBinaryMessage.cs b/RTM/RTM/Public/AVIMBinaryMessage.cs deleted file mode 100644 index 103d4b3..0000000 --- a/RTM/RTM/Public/AVIMBinaryMessage.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using LeanCloud.Realtime.Internal; - -namespace LeanCloud.Realtime -{ - /// - /// 基于二进制数据的消息类型,可以直接发送 Byte 数组 - /// - [AVIMMessageClassName("_AVIMBinaryMessage")] - public class AVIMBinaryMessage : AVIMMessage - { - - /// - /// Initializes a new instance of the class. - /// - public AVIMBinaryMessage() - { - - } - /// - /// create new instance of AVIMBinnaryMessage - /// - /// - public AVIMBinaryMessage(byte[] data) - { - this.BinaryData = data; - } - - /// - /// Gets or sets the binary data. - /// - /// The binary data. - public byte[] BinaryData { get; set; } - - internal override MessageCommand BeforeSend(MessageCommand cmd) - { - var result = base.BeforeSend(cmd); - result = result.Binary(this.BinaryData); - return result; - } - } -} diff --git a/RTM/RTM/Public/AVIMClient.cs b/RTM/RTM/Public/AVIMClient.cs deleted file mode 100644 index 4715c07..0000000 --- a/RTM/RTM/Public/AVIMClient.cs +++ /dev/null @@ -1,1195 +0,0 @@ -using LeanCloud; -using LeanCloud.Storage.Internal; -using LeanCloud.Realtime.Internal; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud.Realtime -{ - /// - /// 代表一个实时通信的终端用户 - /// - public class AVIMClient - { - private readonly string clientId; - private readonly AVRealtime _realtime; - internal readonly object mutex = new object(); - internal readonly object patchMutex = new object(); - - /// - /// 一些可变的配置选项,便于应对各种需求场景 - /// - public struct Configuration - { - /// - /// Gets or sets a value indicating whether this - /// auto read. - /// - /// true if auto read; otherwise, false. - public bool AutoRead { get; set; } - } - - /// - /// Gets or sets the current configuration. - /// - /// The current configuration. - public Configuration CurrentConfiguration - { - get; set; - } - - internal AVRealtime LinkedRealtime - { - get { return _realtime; } - } - - /// - /// 单点登录所使用的 Tag - /// - public string Tag - { - get; - private set; - } - - /// - /// 客户端的标识,在一个 Application 内唯一。 - /// - public string ClientId - { - get { return clientId; } - } - - //private EventHandler m_OnNoticeReceived; - ///// - ///// 接收到服务器的命令时触发的事件 - ///// - //public event EventHandler OnNoticeReceived - //{ - // add - // { - // m_OnNoticeReceived += value; - // } - // remove - // { - // m_OnNoticeReceived -= value; - // } - //} - - private int onMessageReceivedCount = 0; - private EventHandler m_OnMessageReceived; - /// - /// 接收到聊天消息的事件通知 - /// - public event EventHandler OnMessageReceived - { - add - { - onMessageReceivedCount++; - AVRealtime.PrintLog("AVIMClient.OnMessageReceived event add with " + onMessageReceivedCount + " times"); - m_OnMessageReceived += value; - } - remove - { - onMessageReceivedCount--; - AVRealtime.PrintLog("AVIMClient.OnMessageReceived event remove with" + onMessageReceivedCount + " times"); - m_OnMessageReceived -= value; - } - } - - /// - /// Occurs when on members joined. - /// - public event EventHandler OnMembersJoined; - - /// - /// Occurs when on members left. - /// - public event EventHandler OnMembersLeft; - - /// - /// Occurs when on kicked. - /// - public event EventHandler OnKicked; - - /// - /// Occurs when on invited. - /// - public event EventHandler OnInvited; - - internal event EventHandler OnOfflineMessageReceived; - - private EventHandler m_OnSessionClosed; - /// - /// 当前打开的链接被迫关闭时触发的事件回调 - /// 可能的原因有单点登录冲突,或者被 REST API 强制踢下线 - /// - public event EventHandler OnSessionClosed - { - add - { - m_OnSessionClosed += value; - } - remove - { - m_OnSessionClosed -= value; - } - } - - /// - /// 创建 AVIMClient 对象 - /// - /// - /// - internal AVIMClient(string clientId, AVRealtime realtime) - : this(clientId, null, realtime) - { - - } - - /// - /// - /// - /// - /// - /// - internal AVIMClient(string clientId, string tag, AVRealtime realtime) - { - this.clientId = clientId; - Tag = tag ?? tag; - _realtime = realtime; - - #region sdk 强制在接收到消息之后一定要向服务端回发 ack - var ackListener = new AVIMMessageListener(); - ackListener.OnMessageReceived += AckListener_OnMessageReceieved; - //this.RegisterListener(ackListener); - #endregion - - #region 默认要为当前 client 绑定一个消息的监听器,用作消息的事件通知 - var messageListener = new AVIMMessageListener(); - messageListener.OnMessageReceived += MessageListener_OnMessageReceived; - this.RegisterListener(messageListener); - #endregion - - #region 默认要为当前 client 绑定一个 session close 的监听器,用来监测单点登录冲突的事件通知 - var sessionListener = new SessionListener(); - sessionListener.OnSessionClosed += SessionListener_OnSessionClosed; - this.RegisterListener(sessionListener); - #endregion - - #region 默认要为当前 client 监听 Ta 所出的对话中的人员变动的被动消息通知 - var membersJoinedListener = new AVIMMembersJoinListener(); - membersJoinedListener.OnMembersJoined += MembersJoinedListener_OnMembersJoined; - this.RegisterListener(membersJoinedListener); - - var membersLeftListener = new AVIMMembersLeftListener(); - membersLeftListener.OnMembersLeft += MembersLeftListener_OnMembersLeft; - this.RegisterListener(membersLeftListener); - - var invitedListener = new AVIMInvitedListener(); - invitedListener.OnInvited += InvitedListener_OnInvited; - this.RegisterListener(invitedListener); - - var kickedListener = new AVIMKickedListener(); - kickedListener.OnKicked += KickedListener_OnKicked; - this.RegisterListener(kickedListener); - #endregion - - #region 当前 client id 离线的时间内,TA 所在的对话产生的普通消息会以离线消息的方式送达到 TA 下一次登录的客户端 - var offlineMessageListener = new OfflineMessageListener(); - offlineMessageListener.OnOfflineMessageReceived += OfflineMessageListener_OnOfflineMessageReceived; - this.RegisterListener(offlineMessageListener); - #endregion - - #region 当前 client 离线期间内产生的未读消息可以通过之后调用 Conversation.SyncStateAsync 获取一下离线期间内的未读状态 - var unreadListener = new ConversationUnreadListener(); - this.RegisterListener(unreadListener); - #endregion - - #region 消息补丁(修改或者撤回) - var messagePatchListener = new MessagePatchListener(); - messagePatchListener.OnReceived = (messages) => - { - foreach (var message in messages) { - if (message is AVIMRecalledMessage) { - m_OnMessageRecalled?.Invoke(this, new AVIMMessagePatchEventArgs(message)); - } else { - m_OnMessageUpdated?.Invoke(this, new AVIMMessagePatchEventArgs(message)); - } - } - }; - this.RegisterListener(messagePatchListener); - #endregion - - #region configuration - CurrentConfiguration = new Configuration() - { - AutoRead = true, - }; - #endregion - - } - - private void OfflineMessageListener_OnOfflineMessageReceived(object sender, AVIMMessageEventArgs e) - { - if (OnOfflineMessageReceived != null) - { - OnOfflineMessageReceived(this, e); - } - this.AckListener_OnMessageReceieved(sender, e); - } - - private void KickedListener_OnKicked(object sender, AVIMOnKickedEventArgs e) - { - if (OnKicked != null) - OnKicked(this, e); - } - - private void InvitedListener_OnInvited(object sender, AVIMOnInvitedEventArgs e) - { - if (OnInvited != null) - OnInvited(this, e); - } - - private void MembersLeftListener_OnMembersLeft(object sender, AVIMOnMembersLeftEventArgs e) - { - if (OnMembersLeft != null) - OnMembersLeft(this, e); - } - - private void MembersJoinedListener_OnMembersJoined(object sender, AVIMOnMembersJoinedEventArgs e) - { - if (OnMembersJoined != null) - OnMembersJoined(this, e); - } - - private void SessionListener_OnSessionClosed(int arg1, string arg2, string arg3) - { - if (m_OnSessionClosed != null) - { - var args = new AVIMSessionClosedEventArgs() - { - Code = arg1, - Reason = arg2, - Detail = arg3 - }; - if (args.Code == 4115 || args.Code == 4111) - { - this._realtime.sessionConflict = true; - } - - m_OnSessionClosed(this, args); - } - AVRealtime.PrintLog("SessionListener_OnSessionClosed invoked."); - //this.LinkedRealtime.LogOut(); - } - - private void MessageListener_OnMessageReceived(object sender, AVIMMessageEventArgs e) - { - if (this.m_OnMessageReceived != null) - { - this.m_OnMessageReceived.Invoke(this, e); - } - this.AckListener_OnMessageReceieved(sender, e); - } - - private void AckListener_OnMessageReceieved(object sender, AVIMMessageEventArgs e) - { - lock (mutex) - { - var ackCommand = new AckCommand().MessageId(e.Message.Id) - .ConversationId(e.Message.ConversationId); - - // 在 v.2 协议下,只要在线收到消息,就默认是已读的,下次上线不会再把当前消息当做未读消息 - if (this.LinkedRealtime.CurrentConfiguration.OfflineMessageStrategy == AVRealtime.OfflineMessageStrategy.UnreadNotice) - { - ackCommand = ackCommand.ReadAck(); - } - - this.RunCommandAsync(ackCommand); - } - } - - private void UpdateUnreadNotice(object sender, AVIMMessageEventArgs e) - { - ConversationUnreadListener.UpdateNotice(e.Message); - } - - #region listener - - /// - /// 注册 IAVIMListener - /// - /// - /// - public void RegisterListener(IAVIMListener listener, Func runtimeHook = null) - { - _realtime.SubscribeNoticeReceived(listener, runtimeHook); - } - - #region get client instance - /// - /// Get the specified clientId. - /// - /// The get. - /// Client identifier. - public static AVIMClient Get(string clientId) - { - if (AVRealtime.clients == null || !AVRealtime.clients.ContainsKey(clientId)) throw new Exception(string.Format("no client found with a id in {0}", clientId)); - - return AVRealtime.clients[clientId]; - } - #endregion - - #endregion - /// - /// 创建对话 - /// - /// 对话 - /// 是否创建唯一对话,当 isUnique 为 true 时,如果当前已经有相同成员的对话存在则返回该对话,否则会创建新的对话。该值默认为 false。 - /// - internal Task CreateConversationAsync(AVIMConversation conversation, bool isUnique = true) - { - var cmd = new ConversationCommand() - .Generate(conversation) - .Unique(isUnique); - - var convCmd = cmd.Option("start") - .PeerId(clientId); - - return LinkedRealtime.AttachSignature(convCmd, LinkedRealtime.SignatureFactory.CreateStartConversationSignature(this.clientId, conversation.MemberIds)).OnSuccess(_ => - { - return this.RunCommandAsync(convCmd).OnSuccess(t => - { - var result = t.Result; - if (result.Item1 < 1) - { - var members = conversation.MemberIds.ToList(); - members.Add(ClientId); - conversation.MemberIds = members; - conversation.MergeFromPushServer(result.Item2); - } - - return conversation; - }); - }).Unwrap(); - } - - /// - /// 创建与目标成员的对话. - /// - /// 返回对话实例. - /// 目标成员. - /// 目标成员列表. - /// 对话名称. - /// 是否是系统对话. - /// 是否为暂态对话(聊天室). - /// 是否是唯一对话. - /// 自定义属性. - public Task CreateConversationAsync(string member = null, - IEnumerable members = null, - string name = "", - bool isSystem = false, - bool isTransient = false, - bool isUnique = true, - bool isTemporary = false, - int ttl = 86400, - IDictionary options = null) - { - if (member == null) member = ClientId; - var membersAsList = Concat(member, members, "创建对话时被操作的 member(s) 不可以为空。"); - var conversation = new AVIMConversation(members: membersAsList, - name: name, - isUnique: isUnique, - isSystem: isSystem, - isTransient: isTransient, - isTemporary: isTemporary, - ttl: ttl, - client: this); - if (options != null) - { - foreach (var key in options.Keys) - { - conversation[key] = options[key]; - } - } - return CreateConversationAsync(conversation, isUnique); - } - - /// - /// Creates the conversation async. - /// - /// The conversation async. - /// Builder. - public Task CreateConversationAsync(IAVIMConversatioBuilder builder) - { - var conversation = builder.Build(); - return CreateConversationAsync(conversation, conversation.IsUnique); - } - - /// - /// Gets the conversatio builder. - /// - /// The conversatio builder. - public AVIMConversationBuilder GetConversationBuilder() - { - var builder = AVIMConversationBuilder.CreateDefaultBuilder(); - builder.Client = this; - return builder; - } - - /// - /// 创建虚拟对话,对话 id 是由本地直接生成,云端根据规则消息发送给指定的 client id(s) - /// - /// - /// - /// 过期时间,默认是一天(86400 秒),单位是秒 - /// - public Task CreateTemporaryConversationAsync(string member = null, - IEnumerable members = null, int ttl = 86400) - { - if (member == null) member = ClientId; - var membersAsList = Concat(member, members, "创建对话时被操作的 member(s) 不可以为空。"); - return CreateConversationAsync(member, membersAsList, isTemporary: true, ttl: ttl); - } - - /// - /// 创建聊天室(即:暂态对话) - /// - /// 聊天室名称 - /// - public Task CreateChatRoomAsync(string chatroomName) - { - return CreateConversationAsync(name: chatroomName, isTransient: true); - } - - /// - /// 获取一个对话 - /// - /// 对话的 ID - /// 从服务器获取 - /// - public Task GetConversationAsync(string id, bool noCache = true) - { - if (!noCache) return Task.FromResult(new AVIMConversation(this) { ConversationId = id }); - else - { - return this.GetQuery().WhereEqualTo("objectId", id).FirstAsync(); - } - } - - #region send message - /// - /// 向目标对话发送消息 - /// - /// 目标对话 - /// 消息体 - /// - public Task SendMessageAsync( - AVIMConversation conversation, - IAVIMMessage message) - { - return this.SendMessageAsync(conversation, message, new AVIMSendOptions() - { - Receipt = true, - Transient = false, - Priority = 1, - Will = false, - PushData = null, - }); - } - - /// - /// 向目标对话发送消息 - /// - /// 目标对话 - /// 消息体 - /// 消息的发送选项,包含了一些特殊的标记 - /// - public Task SendMessageAsync( - AVIMConversation conversation, - IAVIMMessage message, - AVIMSendOptions options) - { - if (this.LinkedRealtime.State != AVRealtime.Status.Online) throw new Exception("未能连接到服务器,无法发送消息。"); - - var messageBody = message.Serialize(); - - message.ConversationId = conversation.ConversationId; - message.FromClientId = this.ClientId; - - var cmd = new MessageCommand() - .Message(messageBody) - .ConvId(conversation.ConversationId) - .Receipt(options.Receipt) - .Transient(options.Transient) - .Priority(options.Priority) - .Will(options.Will) - .MentionAll(message.MentionAll); - - if (message is AVIMMessage) - { - cmd = ((AVIMMessage)message).BeforeSend(cmd); - } - - if (options.PushData != null) - { - cmd = cmd.PushData(options.PushData); - } - - if (message.MentionList != null) - { - cmd = cmd.Mention(message.MentionList); - } - - var directCmd = cmd.PeerId(this.ClientId); - - return this.RunCommandAsync(directCmd).OnSuccess(t => - { - var response = t.Result.Item2; - - message.Id = response["uid"].ToString(); - message.ServerTimestamp = long.Parse(response["t"].ToString()); - - return message; - - }); - } - - - #endregion - - #region mute & unmute - /// - /// 当前用户对目标对话进行静音操作 - /// - /// - /// - public Task MuteConversationAsync(AVIMConversation conversation) - { - var convCmd = new ConversationCommand() - .ConversationId(conversation.ConversationId) - .Option("mute") - .PeerId(this.ClientId); - - return this.RunCommandAsync(convCmd); - } - /// - /// 当前用户对目标对话取消静音,恢复该对话的离线消息推送 - /// - /// - /// - public Task UnmuteConversationAsync(AVIMConversation conversation) - { - var convCmd = new ConversationCommand() - .ConversationId(conversation.ConversationId) - .Option("unmute") - .PeerId(this.ClientId); - - return this.RunCommandAsync(convCmd); - } - #endregion - - #region Conversation members operations - internal Task OperateMembersAsync(AVIMConversation conversation, string action, string member = null, IEnumerable members = null) - { - if (string.IsNullOrEmpty(conversation.ConversationId)) - { - throw new Exception("conversation id 不可以为空。"); - } - - var membersAsList = Concat(member, members, "加人或者踢人的时候,被操作的 member(s) 不可以为空。"); - - var cmd = new ConversationCommand().ConversationId(conversation.ConversationId) - .Members(membersAsList) - .Option(action) - .PeerId(clientId); - - return this.LinkedRealtime.AttachSignature(cmd, LinkedRealtime.SignatureFactory.CreateConversationSignature(conversation.ConversationId, ClientId, membersAsList, ConversationSignatureAction.Add)).OnSuccess(_ => - { - return this.RunCommandAsync(cmd).OnSuccess(t => - { - var result = t.Result; - if (!conversation.IsTransient) - { - if (conversation.MemberIds == null) conversation.MemberIds = new List(); - conversation.MemberIds = conversation.MemberIds.Concat(membersAsList); - } - return result; - }); - }).Unwrap(); - } - internal IEnumerable Concat(T single, IEnumerable collection, string exString = null) - { - List asList = null; - if (collection == null) - { - collection = new List(); - } - asList = collection.ToList(); - if (asList.Count == 0 && single == null) - { - exString = exString ?? "can not cancat a collection with a null value."; - throw new ArgumentNullException(exString); - } - asList.Add(single); - return asList; - } - - #region Join - /// - /// 当前用户加入到目标的对话中 - /// - /// 目标对话 - /// - public Task JoinAsync(AVIMConversation conversation) - { - return this.OperateMembersAsync(conversation, "add", this.ClientId); - } - #endregion - - #region Invite - /// - /// 直接将其他人加入到目标对话 - /// 被操作的人会在客户端会触发 OnInvited 事件,而已经存在于对话的用户会触发 OnMembersJoined 事件 - /// - /// 目标对话 - /// 单个的 Client Id - /// Client Id 集合 - /// - public Task InviteAsync(AVIMConversation conversation, string member = null, IEnumerable members = null) - { - return this.OperateMembersAsync(conversation, "add", member, members); - } - #endregion - - #region Left - /// - /// 当前 Client 离开目标对话 - /// 可以理解为是 QQ 群的退群操作 - /// - /// - /// 目标对话 - /// - [Obsolete("use LeaveAsync instead.")] - public Task LeftAsync(AVIMConversation conversation) - { - return this.OperateMembersAsync(conversation, "remove", this.ClientId); - } - - /// - /// Leaves the conversation async. - /// - /// The async. - /// Conversation. - public Task LeaveAsync(AVIMConversation conversation) - { - return this.OperateMembersAsync(conversation, "remove", this.ClientId); - } - #endregion - - #region Kick - /// - /// 从目标对话中剔除成员 - /// - /// 目标对话 - /// 被剔除的单个成员 - /// 被剔除的成员列表 - /// - public Task KickAsync(AVIMConversation conversation, string member = null, IEnumerable members = null) - { - return this.OperateMembersAsync(conversation, "remove", member, members); - } - #endregion - - #endregion - - #region Query && Message history && ack - - /// - /// Get conversation query. - /// - /// - public AVIMConversationQuery GetQuery() - { - return GetConversationQuery(); - } - - /// - /// Get conversation query. - /// - /// The conversation query. - public AVIMConversationQuery GetConversationQuery() - { - return new AVIMConversationQuery(this); - } - - #region load message history - - /// - /// 查询目标对话的历史消息 - /// 不支持聊天室(暂态对话) - /// - /// 目标对话 - /// 从 beforeMessageId 开始向前查询(和 beforeTimeStampPoint 共同使用,为防止某毫秒时刻有重复消息) - /// 截止到某个 afterMessageId (不包含) - /// 从 beforeTimeStampPoint 开始向前查询 - /// 拉取截止到 afterTimeStampPoint 时间戳(不包含) - /// 查询方向,默认是 1,如果是 1 表示从新消息往旧消息方向, 0 则相反,其他值无效 - /// 拉取消息条数,默认值 20 条,可设置为 1 - 1000 之间的任意整数 - /// - public Task> QueryMessageAsync(AVIMConversation conversation, - string beforeMessageId = null, - string afterMessageId = null, - DateTime? beforeTimeStampPoint = null, - DateTime? afterTimeStampPoint = null, - int direction = 1, - int limit = 20) - where T : IAVIMMessage - { - var maxLimit = 1000; - var actualLimit = limit > maxLimit ? maxLimit : limit; - var logsCmd = new AVIMCommand() - .Command("logs") - .Argument("cid", conversation.ConversationId) - .Argument("l", actualLimit); - - if (beforeMessageId != null) - { - logsCmd = logsCmd.Argument("mid", beforeMessageId); - } - - if (afterMessageId != null) - { - logsCmd = logsCmd.Argument("tmid", afterMessageId); - } - - if (beforeTimeStampPoint != null && beforeTimeStampPoint.Value != DateTime.MinValue) - { - logsCmd = logsCmd.Argument("t", beforeTimeStampPoint.Value.ToUnixTimeStamp()); - } - - if (afterTimeStampPoint != null && afterTimeStampPoint.Value != DateTime.MinValue) - { - logsCmd = logsCmd.Argument("tt", afterTimeStampPoint.Value.ToUnixTimeStamp()); - } - - if (direction == 0) - { - logsCmd = logsCmd.Argument("direction", "NEW"); - } - - var subMessageType = typeof(T); - var subTypeInteger = subMessageType == typeof(AVIMTypedMessage) ? 0 : FreeStyleMessageClassInfo.GetTypedInteger(subMessageType.GetTypeInfo()); - - if (subTypeInteger != 0) - { - logsCmd = logsCmd.Argument("lctype", subTypeInteger); - } - - return this.RunCommandAsync(logsCmd).OnSuccess(t => - { - var rtn = new List(); - var result = t.Result.Item2; - var logs = result["logs"] as List; - if (logs != null) - { - foreach (var log in logs) - { - var logMap = log as IDictionary; - if (logMap != null) - { - var msgStr = logMap["data"].ToString(); - var messageObj = AVRealtime.FreeStyleMessageClassingController.Instantiate(msgStr, logMap); - messageObj.ConversationId = conversation.ConversationId; - rtn.Add(messageObj); - } - } - } - - conversation.OnMessageLoad(rtn); - - return rtn.AsEnumerable().OfType(); - }); - } - #endregion - - - //public Task MarkAsReadAsync(string conversationId = null, string messageId = null, AVIMConversation conversation = null, AVIMMessage message = null) - //{ - // var msgId = messageId != null ? messageId : message.Id; - // var convId = conversationId != null ? conversationId : conversation.ConversationId; - // if (convId == null && msgId == null) throw new ArgumentNullException("发送已读回执的时候,必须指定 conversation id 或者 message id"); - // lock (mutex) - // { - // var ackCommand = new AckCommand() - // .ReadAck().MessageId(msgId) - // .ConversationId(convId) - // .PeerId(this.ClientId); - - // return this.RunCommandAsync(ackCommand); - // } - //} - #region 查询对话中对方的接收状态,也就是已读回执 - private Task> FetchAllReceiptTimestampsAsync(string targetClientId = null, string conversationId = null, AVIMConversation conversation = null, bool queryAllMembers = false) - { - var convId = conversationId != null ? conversationId : conversation.ConversationId; - if (convId == null) throw new ArgumentNullException("conversationId 和 conversation 不可以同时为 null"); - - var cmd = new ConversationCommand().ConversationId(convId) - .TargetClientId(targetClientId) - .QueryAllMembers(queryAllMembers) - .Option("max-read") - .PeerId(clientId); - - return this.RunCommandAsync(cmd).OnSuccess(t => - { - var result = t.Result; - long maxReadTimestamp = -1; - long maxAckTimestamp = -1; - - if (result.Item2.ContainsKey("maxReadTimestamp")) - { - long.TryParse(result.Item2["maxReadTimestamp"].ToString(), out maxReadTimestamp); - } - if (result.Item2.ContainsKey("maxAckTimestamp")) - { - long.TryParse(result.Item2["maxAckTimestamp"].ToString(), out maxAckTimestamp); - } - return new Tuple(maxAckTimestamp, maxReadTimestamp); - - }); - } - #endregion - - #region 查询对方是否在线 - /// - /// 查询对方 client Id 是否在线 - /// - /// 单个 client Id - /// 多个 client Id 集合 - /// - public Task>> PingAsync(string targetClientId = null, IEnumerable targetClientIds = null) - { - List queryIds = null; - if (targetClientIds != null) queryIds = targetClientIds.ToList(); - if (queryIds == null && string.IsNullOrEmpty(targetClientId)) throw new ArgumentNullException("必须查询至少一个 client id 的状态,targetClientId 和 targetClientIds 不可以同时为空"); - queryIds.Add(targetClientId); - - var cmd = new SessionCommand() - .SessionPeerIds(queryIds) - .Option("query"); - - return this.RunCommandAsync(cmd).OnSuccess(t => - { - var result = t.Result; - List> rtn = new List>(); - var onlineSessionPeerIds = AVDecoder.Instance.DecodeList(result.Item2["onlineSessionPeerIds"]); - foreach (var peerId in targetClientIds) - { - rtn.Add(new Tuple(peerId, onlineSessionPeerIds.Contains(peerId))); - } - return rtn.AsEnumerable(); - }); - } - #endregion - #region 获取暂态对话在线人数 - /// - /// 获取暂态对话(聊天室)在线人数,依赖缓存,并不一定 100% 与真实数据一致。 - /// - /// - /// - public Task CountOnlineClientsAsync(string chatroomId) - { - var command = new AVCommand(relativeUri: "rtm/transient_group/onlines?gid=" + chatroomId, method: "GET", - sessionToken: null, - headers: null, - data: null); - - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).OnSuccess(t => - { - var result = t.Result.Item2; - if (result.ContainsKey("result")) - { - return int.Parse(result["result"].ToString()); - } - return -1; - }); - } - #endregion - #endregion - - #region mark as read - - /// - /// - /// - /// - /// - /// - /// - public Task ReadAsync(AVIMConversation conversation, IAVIMMessage message = null, DateTime? readAt = null) - { - var convRead = new ReadCommand.ConvRead() - { - ConvId = conversation.ConversationId, - }; - - if (message != null) - { - convRead.MessageId = message.Id; - convRead.Timestamp = message.ServerTimestamp; - } - - if (readAt != null && readAt.Value != DateTime.MinValue) - { - convRead.Timestamp = readAt.Value.ToUnixTimeStamp(); - } - - var readCmd = new ReadCommand().Conv(convRead).PeerId(this.ClientId); - - this.RunCommandAsync(readCmd); - - return Task.FromResult(true); - } - - /// - /// mark the conversation as read with conversation id. - /// - /// conversation id - /// - public Task ReadAsync(string conversationId) - { - var conv = AVIMConversation.CreateWithoutData(conversationId, this); - return this.ReadAsync(conv); - } - - /// - /// mark all conversations as read. - /// - /// - public Task ReadAllAsync() - { - var cids = ConversationUnreadListener.FindAllConvIds(); - var readCmd = new ReadCommand().ConvIds(cids).PeerId(this.ClientId); - return this.RunCommandAsync(readCmd); - } - #endregion - - #region recall & modify - - /// - /// Recalls the async. - /// - /// The async. - /// Message. - public Task RecallAsync(IAVIMMessage message) - { - var tcs = new TaskCompletionSource(); - var patchCmd = new PatchCommand().Recall(message); - RunCommandAsync(patchCmd) - .OnSuccess(t => { - var recalledMsg = new AVIMRecalledMessage(); - AVIMMessage.CopyMetaData(message, recalledMsg); - tcs.SetResult(recalledMsg); - }); - return tcs.Task; - } - - /// - /// Modifies the aysnc. - /// - /// The aysnc. - /// 要修改的消息对象 - /// 新的消息对象 - public Task UpdateAsync(IAVIMMessage oldMessage, IAVIMMessage newMessage) - { - var tcs = new TaskCompletionSource(); - var patchCmd = new PatchCommand().Modify(oldMessage, newMessage); - this.RunCommandAsync(patchCmd) - .OnSuccess(t => { - // 从旧消息对象中拷贝数据 - AVIMMessage.CopyMetaData(oldMessage, newMessage); - // 获取更新时间戳 - var response = t.Result.Item2; - if (response.TryGetValue("lastPatchTime", out object updatedAtObj) && - long.TryParse(updatedAtObj.ToString(), out long updatedAt)) { - newMessage.UpdatedAt = updatedAt; - } - tcs.SetResult(newMessage); - }); - return tcs.Task; - } - - internal EventHandler m_OnMessageRecalled; - /// - /// Occurs when on message recalled. - /// - public event EventHandler OnMessageRecalled - { - add - { - this.m_OnMessageRecalled += value; - } - remove - { - this.m_OnMessageRecalled -= value; - } - } - internal EventHandler m_OnMessageUpdated; - /// - /// Occurs when on message modified. - /// - public event EventHandler OnMessageUpdated - { - add - { - this.m_OnMessageUpdated += value; - } - remove - { - this.m_OnMessageUpdated -= value; - } - } - - #endregion - - #region log out - /// - /// 退出登录或者切换账号 - /// - /// - public Task CloseAsync() - { - var cmd = new SessionCommand().Option("close"); - return this.RunCommandAsync(cmd).ContinueWith(t => - { - m_OnSessionClosed(this, null); - }); - } - #endregion - - /// - /// Run command async. - /// - /// The command async. - /// Command. - public Task>> RunCommandAsync(AVIMCommand command) - { - command.PeerId(this.ClientId); - return this.LinkedRealtime.RunCommandAsync(command); - } - - /// - /// Run command. - /// - /// Command. - public void RunCommand(AVIMCommand command) - { - command.PeerId(this.ClientId); - this.LinkedRealtime.RunCommand(command); - } - } - - /// - /// AVIMClient extensions. - /// - public static class AVIMClientExtensions - { - /// - /// Create conversation async. - /// - /// The conversation async. - /// Client. - /// Members. - public static Task CreateConversationAsync(this AVIMClient client, IEnumerable members) - { - return client.CreateConversationAsync(members: members); - } - - public static Task CreateConversationAsync(this AVIMClient client, IEnumerable members, string conversationName) - { - return client.CreateConversationAsync(members: members, name: conversationName); - } - - /// - /// Get conversation. - /// - /// The conversation. - /// Client. - /// Conversation identifier. - public static AVIMConversation GetConversation(this AVIMClient client, string conversationId) - { - return AVIMConversation.CreateWithoutData(conversationId, client); - } - - /// - /// Join conversation async. - /// - /// The async. - /// Client. - /// Conversation identifier. - public static Task JoinAsync(this AVIMClient client, string conversationId) - { - var conversation = client.GetConversation(conversationId); - return client.JoinAsync(conversation); - } - - /// - /// Leave conversation async. - /// - /// The async. - /// Client. - /// Conversation identifier. - public static Task LeaveAsync(this AVIMClient client, string conversationId) - { - var conversation = client.GetConversation(conversationId); - return client.LeaveAsync(conversation); - } - - /// - /// Query messages. - /// - /// The message async. - /// Client. - /// Conversation. - /// Before message identifier. - /// After message identifier. - /// Before time stamp point. - /// After time stamp point. - /// Direction. - /// Limit. - public static Task> QueryMessageAsync(this AVIMClient client, - AVIMConversation conversation, - string beforeMessageId = null, - string afterMessageId = null, - DateTime? beforeTimeStampPoint = null, - DateTime? afterTimeStampPoint = null, - int direction = 1, - int limit = 20) - { - return client.QueryMessageAsync(conversation, - beforeMessageId, - afterMessageId, - beforeTimeStampPoint, - afterTimeStampPoint, - direction, - limit); - } - - /// - /// Get the chat room query. - /// - /// The chat room query. - /// Client. - public static AVIMConversationQuery GetChatRoomQuery(this AVIMClient client) - { - return client.GetQuery().WhereEqualTo("tr", true); - } - } -} diff --git a/RTM/RTM/Public/AVIMConversation.cs b/RTM/RTM/Public/AVIMConversation.cs deleted file mode 100644 index decc8e6..0000000 --- a/RTM/RTM/Public/AVIMConversation.cs +++ /dev/null @@ -1,1545 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using LeanCloud.Realtime.Internal; -using LeanCloud; -using LeanCloud.Storage.Internal; -using System.Collections; -using System.IO; - -namespace LeanCloud.Realtime -{ - /// - /// 对话 - /// - public class AVIMConversation : AVObject - { - private DateTime? updatedAt; - - private DateTime? createdAt; - - private DateTime? lastMessageAt; - - internal DateTime? expiredAt; - - private string name; - - private AVObject convState; - - internal readonly Object mutex = new Object(); - //private readonly IDictionary estimatedData = new Dictionary(); - - internal AVIMClient _currentClient; - - IEnumerator> IEnumerable>.GetEnumerator() - { - lock (mutex) - { - return ((IEnumerable>)convState).GetEnumerator(); - } - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - lock (mutex) - { - return ((IEnumerable>)convState).GetEnumerator(); - } - } - - virtual public object this[string key] - { - get - { - return convState[key]; - } - set - { - convState[key] = value; - } - } - public ICollection Keys - { - get - { - lock (mutex) - { - return convState.Keys; - } - } - } - public T Get(string key) - { - return this.convState.Get(key); - } - public bool ContainsKey(string key) - { - return this.convState.ContainsKey(key); - } - - internal IDictionary EncodeAttributes() - { - var currentOperations = convState.StartSave(); - var jsonToSave = AVObject.ToJSONObjectForSaving(currentOperations); - return jsonToSave; - } - - internal void MergeFromPushServer(IDictionary json) - { - if (json.Keys.Contains("cdate")) - { - createdAt = DateTime.Parse(json["cdate"].ToString()); - updatedAt = DateTime.Parse(json["cdate"].ToString()); - json.Remove("cdate"); - } - if (json.Keys.Contains("lm")) - { - var ts = long.Parse(json["lm"].ToString()); - updatedAt = ts.ToDateTime(); - lastMessageAt = ts.ToDateTime(); - json.Remove("lm"); - } - if (json.Keys.Contains("c")) - { - Creator = json["c"].ToString(); - json.Remove("c"); - } - if (json.Keys.Contains("m")) - { - MemberIds = json["m"] as IList; - json.Remove("m"); - } - if (json.Keys.Contains("mu")) - { - MuteMemberIds = json["mu"] as IList; - json.Remove("mu"); - } - if (json.Keys.Contains("tr")) - { - IsTransient = bool.Parse(json["tr"].ToString()); - json.Remove("tr"); - } - if (json.Keys.Contains("sys")) - { - IsSystem = bool.Parse(json["sys"].ToString()); - json.Remove("sys"); - } - if (json.Keys.Contains("cid")) - { - ConversationId = json["cid"].ToString(); - json.Remove("cid"); - } - - if (json.Keys.Contains("name")) - { - Name = json["name"].ToString(); - json.Remove("name"); - } - } - - /// - /// 当前的AVIMClient,一个对话理论上只存在一个AVIMClient。 - /// - public AVIMClient CurrentClient - { - get - { - if (_currentClient == null) throw new NullReferenceException("当前对话没有关联有效的 AVIMClient。"); - return _currentClient; - } - //set - //{ - // _currentClient = value; - //} - } - /// - /// 对话的唯一ID - /// - public string ConversationId { get; internal set; } - - /// - /// 对话在全局的唯一的名字 - /// - public string Name - { - get - { - if (convState.ContainsKey("name")) - { - name = this.convState.Get("name"); - } - return name; - } - set - { - if (value == null) - this["name"] = ""; - else - { - this["name"] = value; - } - } - } - - /// - /// 对话中存在的 Client 的 ClientId 列表 - /// - public IEnumerable MemberIds { get; internal set; } - - /// - /// 对该对话静音的成员列表 - /// - /// 对该对话设置了静音的成员,将不会收到离线消息的推送。 - /// - /// - public IEnumerable MuteMemberIds { get; internal set; } - - /// - /// 对话的创建者 - /// - public string Creator { get; private set; } - - /// - /// 是否为聊天室 - /// - public bool IsTransient { get; internal set; } - - /// - /// 是否系统对话 - /// - public bool IsSystem { get; internal set; } - - /// - /// 是否是唯一对话 - /// - public bool IsUnique { get; internal set; } - - /// - /// 对话是否为虚拟对话 - /// - public bool IsTemporary { get; internal set; } - - /// - /// 对话创建的时间 - /// - public DateTime? CreatedAt - { - get - { - DateTime? nullable; - lock (this.mutex) - { - nullable = this.createdAt; - } - return nullable; - } - private set - { - lock (this.mutex) - { - this.createdAt = value; - } - } - } - - /// - /// 对话更新的时间 - /// - public DateTime? UpdatedAt - { - get - { - DateTime? nullable; - lock (this.mutex) - { - nullable = this.updatedAt; - } - return nullable; - } - private set - { - lock (this.mutex) - { - this.updatedAt = value; - } - } - } - - /// - /// 对话中最后一条消息的时间,可以用此判断对话的最后活跃时间 - /// - public DateTime? LastMessageAt - { - get - { - DateTime? nullable; - lock (this.mutex) - { - nullable = this.lastMessageAt; - } - return nullable; - } - private set - { - lock (this.mutex) - { - this.lastMessageAt = value; - } - } - } - - /// - /// 已知 id,在本地构建一个 AVIMConversation 对象 - /// - public AVIMConversation(string id) - : this(id, null) - { - - } - - /// - /// 已知 id 在本地构建一个对话 - /// - /// 对话 id - /// AVIMClient 实例,必须是登陆成功的 - public AVIMConversation(string id, AVIMClient client) : this(client) - { - this.ConversationId = id; - } - - internal AVIMConversation(AVIMClient client) - { - this._currentClient = client; - this.CurrentClient.OnMessageReceived += CurrentClient_OnMessageReceived; - } - - /// - /// AVIMConversation Build 驱动器 - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - internal AVIMConversation(AVIMConversation source = null, - string name = null, - string creator = null, - IEnumerable members = null, - IEnumerable muteMembers = null, - bool isTransient = false, - bool isSystem = false, - IEnumerable> attributes = null, - AVObject state = null, - bool isUnique = true, - bool isTemporary = false, - int ttl = 86400, - AVIMClient client = null) : - this(client) - { - convState = source != null ? source.convState : new AVObject("_Conversation"); - - - this.Name = source?.Name; - this.MemberIds = source?.MemberIds; - this.Creator = source?.Creator; - this.MuteMemberIds = source?.MuteMemberIds; - - if (!string.IsNullOrEmpty(name)) - { - this.Name = name; - } - if (!string.IsNullOrEmpty(creator)) - { - this.Creator = creator; - } - if (members != null) - { - this.MemberIds = members.ToList(); - } - if (muteMembers != null) - { - this.MuteMemberIds = muteMembers.ToList(); - } - - this.IsTransient = isTransient; - this.IsSystem = isSystem; - this.IsUnique = isUnique; - this.IsTemporary = isTemporary; - this.expiredAt = DateTime.Now + new TimeSpan(0, 0, ttl); - - if (state != null) - { - convState = state; - this.ConversationId = state.ObjectId; - this.CreatedAt = state.CreatedAt; - this.UpdatedAt = state.UpdatedAt; - this.MergeMagicFields(state.ToDictionary(x => x.Key, x => x.Value)); - } - - if (attributes != null) - { - this.MergeMagicFields(attributes.ToDictionary(x => x.Key, x => x.Value)); - } - } - - /// - /// 从本地构建一个对话 - /// - /// 对话的 objectId - /// - /// - public static AVIMConversation CreateWithoutData(string convId, AVIMClient client) - { - return new AVIMConversation(client) - { - ConversationId = convId, - }; - } - - /// - /// - /// - /// - /// - /// - public static AVIMConversation CreateWithData(IEnumerable> magicFields, AVIMClient client) - { - if (magicFields is AVObject) - { - return new AVIMConversation(state: (AVObject)magicFields, client: client); - } - return new AVIMConversation(attributes: magicFields, client: client); - } - - #region save to cloud - /// - /// 将修改保存到云端 - /// - /// - public Task SaveAsync() - { - var cmd = new ConversationCommand() - .Generate(this); - - var convCmd = cmd.Option("update") - .PeerId(this.CurrentClient.ClientId); - - return this.CurrentClient.RunCommandAsync(convCmd); - } - #endregion - - #region send message - /// - /// 向该对话发送消息。 - /// - /// 消息体 - /// 是否需要送达回执 - /// 是否是暂态消息,暂态消息不返回送达回执(ack),不保留离线消息,不触发离线推送 - /// 消息等级,默认是1,可选值还有 2 ,3 - /// 标记该消息是否为下线通知消息 - /// 如果消息的接收者已经下线了,这个字段的内容就会被离线推送到接收者 - /// 例如,一张图片消息的离线消息内容可以类似于:[您收到一条图片消息,点击查看] 这样的推送内容,参照微信的做法 - /// - /// - public Task SendMessageAsync(IAVIMMessage avMessage, - bool receipt = true, - bool transient = false, - int priority = 1, - bool will = false, - IDictionary pushData = null) - { - return this.SendMessageAsync(avMessage, new AVIMSendOptions() - { - Receipt = receipt, - Transient = transient, - Priority = priority, - Will = will, - PushData = pushData - }); - } - - /// - /// 发送消息 - /// - /// 消息体 - /// 消息的发送选项,包含了一些特殊的标记 - /// - public Task SendMessageAsync(IAVIMMessage avMessage, AVIMSendOptions options) - { - if (this.CurrentClient == null) throw new Exception("当前对话未指定有效 AVIMClient,无法发送消息。"); - return this.CurrentClient.SendMessageAsync(this, avMessage, options); - } - #endregion - - #region recall message - - #endregion - - #region message listener and event notify - - /// - /// Registers the listener. - /// - /// Listener. - public void RegisterListener(IAVIMListener listener) - { - this.CurrentClient.RegisterListener(listener, this.ConversationIdHook); - } - - internal bool ConversationIdHook(AVIMNotice notice) - { - if (!notice.RawData.ContainsKey("cid")) return false; - return notice.RawData["cid"].ToString() == this.ConversationId; - } - #endregion - - #region mute && unmute - /// - /// 当前用户针对对话做静音操作 - /// - /// - public Task MuteAsync() - { - return this.CurrentClient.MuteConversationAsync(this); - } - /// - /// 当前用户取消对话的静音,恢复该对话的离线消息推送 - /// - /// - public Task UnmuteAsync() - { - return this.CurrentClient.UnmuteConversationAsync(this); - } - #endregion - - #region 成员操作相关接口 - - /// - /// Joins the async. - /// - /// The async. - public Task JoinAsync() - { - return AddMembersAsync(CurrentClient.ClientId); - } - - - /// - /// Adds the members async. - /// - /// The members async. - /// Client identifier. - /// Client identifiers. - public Task AddMembersAsync(string clientId = null, IEnumerable clientIds = null) - { - return this.CurrentClient.InviteAsync(this, clientId, clientIds); - } - - /// - /// Removes the members async. - /// - /// The members async. - /// Client identifier. - /// Client identifiers. - public Task RemoveMembersAsync(string clientId = null, IEnumerable clientIds = null) - { - return this.CurrentClient.KickAsync(this, clientId, clientIds); - } - - /// - /// Quits the async. - /// - /// The async. - public Task QuitAsync() - { - return RemoveMembersAsync(CurrentClient.ClientId); - } - #endregion - - #region load message history - /// - /// 获取当前对话的历史消息 - /// 不支持聊天室(暂态对话) - /// - /// 从 beforeMessageId 开始向前查询(和 beforeTimeStampPoint 共同使用,为防止某毫秒时刻有重复消息) - /// 截止到某个 afterMessageId (不包含) - /// 从 beforeTimeStampPoint 开始向前查询 - /// 拉取截止到 afterTimeStampPoint 时间戳(不包含) - /// 查询方向,默认是 1,如果是 1 表示从新消息往旧消息方向, 0 则相反,其他值无效 - /// 获取的消息数量 - /// - public Task> QueryMessageAsync( - string beforeMessageId = null, - string afterMessageId = null, - DateTime? beforeTimeStampPoint = null, - DateTime? afterTimeStampPoint = null, - int direction = 1, - int limit = 20) - where T : IAVIMMessage - { - return this.CurrentClient.QueryMessageAsync(this, beforeMessageId, afterMessageId, beforeTimeStampPoint, afterTimeStampPoint, direction, limit) - .OnSuccess(t => - { - //OnMessageLoad(t.Result); - return t.Result; - }); - } - - /// - /// Gets the message query. - /// - /// The message query. - public AVIMMessageQuery GetMessageQuery() - { - return new AVIMMessageQuery(this); - } - - /// - /// Gets the message pager. - /// - /// The message pager. - public AVIMMessagePager GetMessagePager() - { - return new AVIMMessagePager(this); - } - - #endregion - - #region 字典与对象之间的转换 - internal virtual void MergeMagicFields(IDictionary data) - { - lock (this.mutex) - { - if (data.ContainsKey("objectId")) - { - this.ConversationId = (data["objectId"] as String); - data.Remove("objectId"); - } - if (data.ContainsKey("createdAt")) - { - this.CreatedAt = AVDecoder.ParseDate(data["createdAt"] as string); - data.Remove("createdAt"); - } - if (data.ContainsKey("updatedAt")) - { - this.updatedAt = AVDecoder.ParseDate(data["updatedAt"] as string); - data.Remove("updatedAt"); - } - if (data.ContainsKey("name")) - { - this.Name = (data["name"] as String); - data.Remove("name"); - } - if (data.ContainsKey("lm")) - { - this.LastMessageAt = AVDecoder.Instance.Decode(data["lm"]) as DateTime?; - data.Remove("lm"); - } - if (data.ContainsKey("m")) - { - this.MemberIds = AVDecoder.Instance.DecodeList(data["m"]); - data.Remove("m"); - } - if (data.ContainsKey("mu")) - { - this.MuteMemberIds = AVDecoder.Instance.DecodeList(data["mu"]); - data.Remove("mu"); - } - if (data.ContainsKey("c")) - { - this.Creator = data["c"].ToString(); - data.Remove("c"); - } - if (data.ContainsKey("unique")) - { - if (data["unique"] is bool) - { - this.IsUnique = (bool)data["unique"]; - } - data.Remove("unique"); - } - foreach (var kv in data) - { - this[kv.Key] = kv.Value; - } - } - } - #endregion - - #region SyncStateAsync & unread & mark as read - /// - /// sync state from server.suhc unread state .etc; - /// - /// - public Task SyncStateAsync() - { - lock (mutex) - { - var rtn = new AggregatedState(); - rtn.Unread = GetUnreadStateFromLocal(); - return Task.FromResult(rtn); - } - } - - private UnreadState _unread; - private UnreadState _lastUnreadWhenOpenSession; - public UnreadState Unread - { - get - { - _lastUnreadWhenOpenSession = GetUnreadStateFromLocal(); - - // v.2 协议,只给出上次离线之后的未读消息,本次在线的收到的消息均视为已读 - if (this.CurrentClient.LinkedRealtime.CurrentConfiguration.OfflineMessageStrategy == AVRealtime.OfflineMessageStrategy.UnreadNotice) - { - _unread = _lastUnreadWhenOpenSession; - } - else if (this.CurrentClient.LinkedRealtime.CurrentConfiguration.OfflineMessageStrategy == AVRealtime.OfflineMessageStrategy.UnreadAck) - { - if (_lastUnreadWhenOpenSession == null) _unread = new UnreadState().MergeReceived(this.Received); - else _unread = _lastUnreadWhenOpenSession.MergeReceived(this.Received); - } - - return _unread; - } - - internal set - { - _unread = value; - } - } - - private readonly object receivedMutex = new object(); - public ReceivedState Received - { - get; set; - } - public ReadState Read - { - get; set; - } - - UnreadState GetUnreadStateFromLocal() - { - lock (mutex) - { - var notice = ConversationUnreadListener.Get(this.ConversationId); - if (notice != null) - { - var unreadState = new UnreadState() - { - LastMessage = notice.LastUnreadMessage, - SyncdAt = ConversationUnreadListener.NotifTime, - Count = notice.UnreadCount - }; - return unreadState; - } - - return null; - } - } - - internal void OnMessageLoad(IEnumerable messages) - { - var lastestInCollection = messages.OrderByDescending(m => m.ServerTimestamp).FirstOrDefault(); - if (lastestInCollection != null) - { - if (CurrentClient.CurrentConfiguration.AutoRead) - { - this.ReadAsync(lastestInCollection); - } - } - } - - /// - /// mark this conversation as read - /// - /// - public Task ReadAsync(IAVIMMessage message = null, DateTime? readAt = null) - { - // 标记已读必须至少是从上一次离线产生的最后一条消息开始,否则无法计算 Count - if (_lastUnreadWhenOpenSession != null) - { - if (_lastUnreadWhenOpenSession.LastMessage != null) - { - message = _lastUnreadWhenOpenSession.LastMessage; - } - } - return this.CurrentClient.ReadAsync(this, message, readAt).OnSuccess(t => - { - Received = null; - _lastUnreadWhenOpenSession = null; - Read = new ReadState() - { - ReadAt = readAt != null ? readAt.Value.ToUnixTimeStamp() : 0, - LastMessage = message, - SyncdAt = DateTime.Now.ToUnixTimeStamp() - }; - - }); - } - - /// - /// aggregated state for the conversation - /// - public class AggregatedState - { - /// - /// Unread state - /// - public UnreadState Unread { get; internal set; } - } - - /// - /// UnreadState recoder for the conversation - /// - public class UnreadState - { - /// - /// unread count - /// - public int Count { get; internal set; } - /// - /// last unread message - /// - public IAVIMMessage LastMessage { get; internal set; } - - /// - /// last sync timestamp - /// - public long SyncdAt { get; internal set; } - - internal UnreadState MergeReceived(ReceivedState receivedState) - { - if (receivedState == null) return this; - var count = Count + receivedState.Count; - var lastMessage = this.LastMessage; - if (receivedState.LastMessage != null) - { - lastMessage = receivedState.LastMessage; - } - var syncdAt = this.SyncdAt > receivedState.SyncdAt ? this.SyncdAt : receivedState.SyncdAt; - return new UnreadState() - { - Count = count, - LastMessage = lastMessage, - SyncdAt = syncdAt - }; - } - } - - public class ReceivedState - { - public int Count { get; internal set; } - /// - /// last received message - /// - public IAVIMMessage LastMessage { get; internal set; } - - /// - /// last sync timestamp - /// - public long SyncdAt { get; internal set; } - } - - public class ReadState - { - public long ReadAt { get; set; } - public IAVIMMessage LastMessage { get; internal set; } - public long SyncdAt { get; internal set; } - } - - #endregion - - #region on client message received to update unread - private void CurrentClient_OnMessageReceived(object sender, AVIMMessageEventArgs e) - { - if (this.CurrentClient.CurrentConfiguration.AutoRead) - { - this.ReadAsync(e.Message); - return; - } - lock (receivedMutex) - { - if (this.Received == null) this.Received = new ReceivedState(); - this.Received.Count++; - this.Received.LastMessage = e.Message; - this.Received.SyncdAt = DateTime.Now.ToUnixTimeStamp(); - } - } - #endregion - } - - /// - /// AVIMConversation extensions. - /// - public static class AVIMConversationExtensions - { - - /// - /// Send message async. - /// - /// The async. - /// Conversation. - /// Message. - /// Options. - public static Task SendAsync(this AVIMConversation conversation, IAVIMMessage message, AVIMSendOptions options) - { - return conversation.SendMessageAsync(message, options); - } - - /// - /// Send message async. - /// - /// The async. - /// Conversation. - /// Message. - /// Options. - /// The 1st type parameter. - public static Task SendAsync(this AVIMConversation conversation, T message, AVIMSendOptions options) - where T : IAVIMMessage - { - return conversation.SendMessageAsync(message, options).OnSuccess(t => - { - return (T)t.Result; - }); - } - - /// - /// Send message async. - /// - /// The async. - /// Conversation. - /// Message. - /// The 1st type parameter. - public static Task SendAsync(this AVIMConversation conversation, T message) - where T : IAVIMMessage - { - return conversation.SendMessageAsync(message).OnSuccess(t => - { - return (T)t.Result; - }); - } - - /// - /// Send text message. - /// - /// The text async. - /// Conversation. - /// Text. - public static Task SendTextAsync(this AVIMConversation conversation, string text) - { - return conversation.SendAsync(new AVIMTextMessage(text)); - } - - #region Image messages - - /// - /// Send image message async. - /// - /// The image async. - /// Conversation. - /// URL. - /// Name. - /// Text title. - /// Custom attributes. - public static Task SendImageAsync(this AVIMConversation conversation, string url, string name = null, string textTitle = null, IDictionary customAttributes = null) - { - return conversation.SendFileMessageAsync(url, name, textTitle, customAttributes); - } - - /// - /// Send image message async. - /// - /// The image async. - /// Conversation. - /// File name. - /// Data. - /// MIME type. - /// Text title. - /// Meta data. - /// Custom attributes. - public static Task SendImageAsync(this AVIMConversation conversation, string fileName, Stream data, string mimeType = null, string textTitle = null, IDictionary metaData = null, IDictionary customAttributes = null) - { - return conversation.SendFileMessageAsync(fileName, data, mimeType, textTitle, metaData, customAttributes); - } - - /// - /// Send image message async. - /// - /// The image async. - /// Conversation. - /// File name. - /// Data. - /// MIME type. - /// Text title. - /// Meta data. - /// Custom attributes. - public static Task SendImageAsync(this AVIMConversation conversation, string fileName, byte[] data, string mimeType = null, string textTitle = null, IDictionary metaData = null, IDictionary customAttributes = null) - { - return conversation.SendFileMessageAsync(fileName, data, mimeType, textTitle, metaData, customAttributes); - } - #endregion - - #region audio message - - /// - /// Send audio message async. - /// - /// The audio async. - /// Conversation. - /// URL. - /// Name. - /// Text title. - /// Custom attributes. - public static Task SendAudioAsync(this AVIMConversation conversation, string url, string name = null, string textTitle = null, IDictionary customAttributes = null) - { - return conversation.SendFileMessageAsync(name, url, textTitle, customAttributes); - } - - /// - /// Send audio message async. - /// - /// The audio async. - /// Conversation. - /// File name. - /// Data. - /// MIME type. - /// Text title. - /// Meta data. - /// Custom attributes. - public static Task SendAudioAsync(this AVIMConversation conversation, string fileName, Stream data, string mimeType = null, string textTitle = null, IDictionary metaData = null, IDictionary customAttributes = null) - { - return conversation.SendFileMessageAsync(fileName, data, mimeType, textTitle, metaData, customAttributes); - } - - /// - /// Send audio message async. - /// - /// The audio async. - /// Conversation. - /// File name. - /// Data. - /// MIME type. - /// Text title. - /// Meta data. - /// Custom attributes. - public static Task SendAudioAsync(this AVIMConversation conversation, string fileName, byte[] data, string mimeType = null, string textTitle = null, IDictionary metaData = null, IDictionary customAttributes = null) - { - return conversation.SendFileMessageAsync(fileName, data, mimeType, textTitle, metaData, customAttributes); - } - #endregion - - #region video message - /// - /// Send video message async. - /// - /// The video async. - /// Conversation. - /// URL. - /// Name. - /// Text title. - /// Custom attributes. - public static Task SendVideoAsync(this AVIMConversation conversation, string url, string name = null, string textTitle = null, IDictionary customAttributes = null) - { - return conversation.SendFileMessageAsync(name, url, textTitle, customAttributes); - } - /// - /// Send video message async. - /// - /// The video async. - /// Conversation. - /// File name. - /// Data. - /// MIME type. - /// Text title. - /// Meta data. - /// Custom attributes. - public static Task SendVideoAsync(this AVIMConversation conversation, string fileName, Stream data, string mimeType = null, string textTitle = null, IDictionary metaData = null, IDictionary customAttributes = null) - { - return conversation.SendFileMessageAsync(fileName, data, mimeType, textTitle, metaData, customAttributes); - } - - /// - /// Send video message async. - /// - /// The video async. - /// Conversation. - /// File name. - /// Data. - /// MIME type. - /// Text title. - /// Meta data. - /// Custom attributes. - public static Task SendVideoAsync(this AVIMConversation conversation, string fileName, byte[] data, string mimeType = null, string textTitle = null, IDictionary metaData = null, IDictionary customAttributes = null) - { - return conversation.SendFileMessageAsync(fileName, data, mimeType, textTitle, metaData, customAttributes); - } - #endregion - - /// - /// Send file message async. - /// - /// The file message async. - /// Conversation. - /// URL. - /// Name. - /// Text title. - /// Custom attributes. - /// The 1st type parameter. - public static Task SendFileMessageAsync(this AVIMConversation conversation, string url, string name = null, string textTitle = null, IDictionary customAttributes = null) - where T : AVIMFileMessage, new() - { - var fileMessage = AVIMFileMessage.FromUrl(name, url, textTitle, customAttributes); - return conversation.SendAsync(fileMessage); - } - - /// - /// Send file message async. - /// - /// The file message async. - /// Conversation. - /// File name. - /// Data. - /// MIME type. - /// Text title. - /// Meta data. - /// Custom attributes. - /// The 1st type parameter. - public static Task SendFileMessageAsync(this AVIMConversation conversation, string fileName, Stream data, string mimeType = null, string textTitle = null, IDictionary metaData = null, IDictionary customAttributes = null) - where T : AVIMFileMessage, new() - { - var fileMessage = AVIMFileMessage.FromStream(fileName, data, mimeType, textTitle, metaData, customAttributes); - - return fileMessage.File.SaveAsync().OnSuccess(fileUploaded => - { - return conversation.SendAsync(fileMessage); - }).Unwrap(); - } - - /// - /// Send file message async. - /// - /// The file message async. - /// Conversation. - /// File name. - /// Data. - /// MIME type. - /// Text title. - /// Meta data. - /// Custom attributes. - /// The 1st type parameter. - public static Task SendFileMessageAsync(this AVIMConversation conversation, string fileName, byte[] data, string mimeType = null, string textTitle = null, IDictionary metaData = null, IDictionary customAttributes = null) - where T : AVIMFileMessage, new() - { - var fileMessage = AVIMFileMessage.FromStream(fileName, new MemoryStream(data), mimeType, textTitle, metaData, customAttributes); - - return conversation.SendFileMessageAsync(fileName, new MemoryStream(data), mimeType, textTitle, metaData, customAttributes); - } - - /// - /// Send location async. - /// - /// The location async. - /// Conversation. - /// Point. - public static Task SendLocationAsync(this AVIMConversation conversation, AVGeoPoint point) - { - var locationMessage = new AVIMLocationMessage(point); - return conversation.SendAsync(locationMessage); - } - - /// - /// Query the message async. - /// - /// The message async. - /// Conversation. - /// Before message identifier. - /// After message identifier. - /// Before time stamp. - /// After time stamp. - /// Direction. - /// Limit. - public static Task> QueryMessageAsync( - this AVIMConversation conversation, - string beforeMessageId = null, - string afterMessageId = null, - long? beforeTimeStamp = null, - long? afterTimeStamp = null, - int direction = 1, - int limit = 20) - { - - return conversation.QueryMessageAsync(beforeMessageId, - afterMessageId, - beforeTimeStamp, - afterTimeStamp, - direction, - limit); - } - - /// - /// Query message with speciafic subclassing type. - /// - /// The message async. - /// Conversation. - /// Before message identifier. - /// After message identifier. - /// Before time stamp. - /// After time stamp. - /// Direction. - /// Limit. - /// The 1st type parameter. - public static Task> QueryMessageAsync( - this AVIMConversation conversation, - string beforeMessageId = null, - string afterMessageId = null, - long? beforeTimeStamp = null, - long? afterTimeStamp = null, - int direction = 1, - int limit = 20) - where T : IAVIMMessage - { - - return conversation.QueryMessageAsync(beforeMessageId, - afterMessageId, - beforeTimeStampPoint: beforeTimeStamp.HasValue ? beforeTimeStamp.Value.ToDateTime() : DateTime.MinValue, - afterTimeStampPoint: afterTimeStamp.HasValue ? afterTimeStamp.Value.ToDateTime() : DateTime.MinValue, - direction: direction, - limit: limit); - } - - /// - /// Query message record before the given message async. - /// - /// The message before async. - /// Conversation. - /// Message. - /// Limit. - public static Task> QueryMessageBeforeAsync( - this AVIMConversation conversation, - IAVIMMessage message, - int limit = 20) - { - return conversation.QueryMessageAsync(beforeMessageId: message.Id, beforeTimeStamp: message.ServerTimestamp, limit: limit); - } - - /// - /// Query message record after the given message async. - /// - /// The message after async. - /// Conversation. - /// Message. - /// Limit. - public static Task> QueryMessageAfterAsync( - this AVIMConversation conversation, - IAVIMMessage message, - int limit = 20) - { - return conversation.QueryMessageAsync(afterMessageId: message.Id, afterTimeStamp: message.ServerTimestamp, limit: limit); - } - - /// - /// Query messages after conversation created. - /// - /// The message after async. - /// Conversation. - /// Limit. - public static Task> QueryMessageFromOldToNewAsync( - this AVIMConversation conversation, - int limit = 20) - { - return conversation.QueryMessageAsync(afterTimeStamp: conversation.CreatedAt.Value.ToUnixTimeStamp(), limit: limit); - } - - - /// - /// Query messages in interval async. - /// - /// The message interval async. - /// Conversation. - /// Start. - /// End. - /// Limit. - public static Task> QueryMessageInIntervalAsync( - this AVIMConversation conversation, - IAVIMMessage start, - IAVIMMessage end, - int limit = 20) - { - return conversation.QueryMessageAsync( - beforeMessageId: end.Id, - beforeTimeStamp: end.ServerTimestamp, - afterMessageId: start.Id, - afterTimeStamp: start.ServerTimestamp, - limit: limit); - } - - /// - /// Recall message async. - /// - /// The async. - /// Conversation. - /// Message. - public static Task RecallAsync(this AVIMConversation conversation, IAVIMMessage message) - { - return conversation.CurrentClient.RecallAsync(message); - } - - /// - /// Modifiy message async. - /// - /// The async. - /// Conversation. - /// 要修改的消息对象 - /// 新的消息对象 - public static Task UpdateAsync(this AVIMConversation conversation, IAVIMMessage oldMessage, IAVIMMessage newMessage) - { - return conversation.CurrentClient.UpdateAsync(oldMessage, newMessage); - } - } - - /// - /// AVIMConversatio builder. - /// - public interface IAVIMConversatioBuilder - { - /// - /// Build this instance. - /// - /// The build. - AVIMConversation Build(); - } - - /// - /// AVIMConversation builder. - /// - public class AVIMConversationBuilder : IAVIMConversatioBuilder - { - /// - /// Gets or sets the client. - /// - /// The client. - public AVIMClient Client { get; internal set; } - private bool isUnique; - private Dictionary properties; - - private string name; - private bool isTransient; - private bool isSystem; - private List members; - - - internal static AVIMConversationBuilder CreateDefaultBuilder() - { - return new AVIMConversationBuilder(); - } - - /// - /// Build this instance. - /// - /// The build. - public AVIMConversation Build() - { - var result = new AVIMConversation( - members: members, - name: name, - isUnique: isUnique, - isSystem: isSystem, - isTransient: isTransient, - client: Client); - - if (properties != null) - { - foreach (var key in properties.Keys) - { - result[key] = properties[key]; - } - } - - return result; - } - - /// - /// Sets the unique. - /// - /// The unique. - /// If set to true toggle. - public AVIMConversationBuilder SetUnique(bool toggle = true) - { - this.isUnique = toggle; - return this; - } - - /// - /// Sets the system. - /// - /// The system. - /// If set to true toggle. - public AVIMConversationBuilder SetSystem(bool toggle = true) - { - this.isSystem = toggle; - return this; - } - - /// - /// Sets the transient. - /// - /// The transient. - /// If set to true toggle. - public AVIMConversationBuilder SetTransient(bool toggle = true) - { - this.isTransient = toggle; - return this; - } - - /// - /// Sets the name. - /// - /// The name. - /// Name. - public AVIMConversationBuilder SetName(string name) - { - this.name = name; - return this; - } - - /// - /// Sets the members. - /// - /// The members. - /// Member client identifiers. - public AVIMConversationBuilder SetMembers(IEnumerable memberClientIds) - { - this.members = memberClientIds.ToList(); - return this; - } - - /// - /// Adds the member. - /// - /// The member. - /// Member client identifier. - public AVIMConversationBuilder AddMember(string memberClientId) - { - return AddMembers(new string[] { memberClientId }); - } - - /// - /// Adds the members. - /// - /// The members. - /// Member client identifiers. - public AVIMConversationBuilder AddMembers(IEnumerable memberClientIds) - { - if (this.members == null) this.members = new List(); - this.members.AddRange(memberClientIds); - return this; - } - - /// - /// Sets the property. - /// - /// The property. - /// Key. - /// Value. - public AVIMConversationBuilder SetProperty(string key, object value) - { - if (this.properties == null) this.properties = new Dictionary(); - this.properties[key] = value; - return this; - } - } - - /// - /// AVIMM essage emitter builder. - /// - public class AVIMMessageEmitterBuilder - { - private AVIMConversation _conversation; - /// - /// Sets the conversation. - /// - /// The conversation. - /// Conversation. - public AVIMMessageEmitterBuilder SetConversation(AVIMConversation conversation) - { - _conversation = conversation; - return this; - } - /// - /// Gets the conversation. - /// - /// The conversation. - public AVIMConversation Conversation - { - get - { - return _conversation; - } - } - - private AVIMSendOptions _sendOptions; - /// - /// Gets the send options. - /// - /// The send options. - public AVIMSendOptions SendOptions - { - get - { - return _sendOptions; - } - } - /// - /// Sets the send options. - /// - /// The send options. - /// Send options. - public AVIMMessageEmitterBuilder SetSendOptions(AVIMSendOptions sendOptions) - { - _sendOptions = sendOptions; - return this; - } - - private IAVIMMessage _message; - /// - /// Gets the message. - /// - /// The message. - public IAVIMMessage Message - { - get - { - return _message; - } - } - /// - /// Sets the message. - /// - /// The message. - /// Message. - public AVIMMessageEmitterBuilder SetMessage(IAVIMMessage message) - { - _message = message; - return this; - } - - /// - /// Send async. - /// - /// The async. - public Task SendAsync() - { - return this.Conversation.SendAsync(this.Message, this.SendOptions); - } - } -} diff --git a/RTM/RTM/Public/AVIMConversationQuery.cs b/RTM/RTM/Public/AVIMConversationQuery.cs deleted file mode 100644 index d5f7284..0000000 --- a/RTM/RTM/Public/AVIMConversationQuery.cs +++ /dev/null @@ -1,299 +0,0 @@ -using LeanCloud.Realtime.Internal; -using LeanCloud.Storage.Internal; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using System.Text.RegularExpressions; - -namespace LeanCloud.Realtime { - /// - /// 对话查询类 - /// - public class AVIMConversationQuery { - internal AVIMClient CurrentClient { get; set; } - - QueryCombinedCondition condition; - - public AVIMConversationQuery() { - condition = new QueryCombinedCondition(); - } - - internal ConversationCommand GenerateQueryCommand() { - var cmd = new ConversationCommand(); - - var queryParameters = BuildParameters(); - if (queryParameters != null) { - if (queryParameters.TryGetValue("where", out object where)) { - cmd.Where(where); - } - if (queryParameters.TryGetValue("skip", out object skip)) { - cmd.Skip((int)skip); - } - if (queryParameters.TryGetValue("limit", out object limit)) { - cmd.Limit((int)limit); - } - if (queryParameters.TryGetValue("order", out object order)) { - cmd.Sort(order.ToString()); - } - } - - return cmd; - } - - #region Combined Query - - public static AVIMConversationQuery And(IEnumerable queries) { - AVIMConversationQuery composition = new AVIMConversationQuery(); - if (queries != null) { - foreach (AVIMConversationQuery query in queries) { - composition.condition.AddCondition(query.condition); - } - } - return composition; - } - - public static AVIMConversationQuery Or(IEnumerable queries) { - AVIMConversationQuery composition = new AVIMConversationQuery { - condition = new QueryCombinedCondition(QueryCombinedCondition.OR) - }; - if (queries != null) { - foreach (AVIMConversationQuery query in queries) { - composition.condition.AddCondition(query.condition); - } - } - return composition; - } - - #endregion - - public Task CountAsync() { - var convCmd = GenerateQueryCommand(); - convCmd.Count(); - convCmd.Limit(0); - var cmd = convCmd.Option("query"); - return CurrentClient.RunCommandAsync(convCmd).OnSuccess(t => { - var result = t.Result.Item2; - - if (result.ContainsKey("count")) { - return int.Parse(result["count"].ToString()); - } - return 0; - }); - } - - - /// - /// 查找符合条件的对话 - /// - /// - public Task> FindAsync() { - var convCmd = GenerateQueryCommand().Option("query"); - return CurrentClient.RunCommandAsync(convCmd).OnSuccess(t => { - var result = t.Result.Item2; - - IList rtn = new List(); - if (result["results"] is IList conList) { - foreach (var c in conList) { - if (c is IDictionary cData) { - var con = AVIMConversation.CreateWithData(cData, CurrentClient); - rtn.Add(con); - } - } - } - return rtn.AsEnumerable(); - }); - } - - public Task FirstAsync() { - return FirstOrDefaultAsync(); - } - - public Task FirstOrDefaultAsync() { - var firstQuery = Limit(1); - return firstQuery.FindAsync().OnSuccess(t => { - return t.Result.FirstOrDefault(); - }); - } - - public Task GetAsync(string objectId) { - var idQuery = WhereEqualTo("objectId", objectId); - return idQuery.FirstAsync(); - } - - public AVIMConversationQuery OrderBy(string key) { - condition.OrderBy(key); - return this; - } - - public AVIMConversationQuery OrderByDescending(string key) { - condition.OrderByDescending(key); - return this; - } - - public AVIMConversationQuery Include(string key) { - condition.Include(key); - return this; - } - - public AVIMConversationQuery Select(string key) { - condition.Select(key); - return this; - } - - public AVIMConversationQuery Skip(int count) { - condition.Skip(count); - return this; - } - - public AVIMConversationQuery Limit(int count) { - condition.Limit(count); - return this; - } - - #region Where - - public AVIMConversationQuery WhereContainedIn(string key, IEnumerable values) { - condition.WhereContainedIn(key, values); - return this; - } - - public AVIMConversationQuery WhereContainsAll(string key, IEnumerable values) { - condition.WhereContainsAll(key, values); - return this; - } - - public AVIMConversationQuery WhereContains(string key, string substring) { - condition.WhereContains(key, substring); - return this; - } - - public AVIMConversationQuery WhereDoesNotExist(string key) { - condition.WhereDoesNotExist(key); - return this; - } - - public AVIMConversationQuery WhereDoesNotMatchQuery(string key, AVQuery query) where TOther : AVObject { - condition.WhereDoesNotMatchQuery(key, query); - return this; - } - - public AVIMConversationQuery WhereEndsWith(string key, string suffix) { - condition.WhereEndsWith(key, suffix); - return this; - } - - public AVIMConversationQuery WhereEqualTo(string key, object value) { - condition.WhereEqualTo(key, value); - return this; - } - - public AVIMConversationQuery WhereSizeEqualTo(string key, uint size) { - condition.WhereSizeEqualTo(key, size); - return this; - } - - public AVIMConversationQuery WhereExists(string key) { - condition.WhereExists(key); - return this; - } - - public AVIMConversationQuery WhereGreaterThan(string key, object value) { - condition.WhereGreaterThan(key, value); - return this; - } - - public AVIMConversationQuery WhereGreaterThanOrEqualTo(string key, object value) { - condition.WhereGreaterThanOrEqualTo(key, value); - return this; - } - - public AVIMConversationQuery WhereLessThan(string key, object value) { - condition.WhereLessThan(key, value); - return this; - } - - public AVIMConversationQuery WhereLessThanOrEqualTo(string key, object value) { - condition.WhereLessThanOrEqualTo(key, value); - return this; - } - - public AVIMConversationQuery WhereMatches(string key, Regex regex, string modifiers) { - condition.WhereMatches(key, regex, modifiers); - return this; - } - - public AVIMConversationQuery WhereMatches(string key, Regex regex) { - return WhereMatches(key, regex, null); - } - - public AVIMConversationQuery WhereMatches(string key, string pattern, string modifiers) { - return WhereMatches(key, new Regex(pattern, RegexOptions.ECMAScript), modifiers); - } - - public AVIMConversationQuery WhereMatches(string key, string pattern) { - return WhereMatches(key, pattern, null); - } - - public AVIMConversationQuery WhereMatchesKeyInQuery(string key, string keyInQuery, AVQuery query) where TOther : AVObject { - condition.WhereMatchesKeyInQuery(key, keyInQuery, query); - return this; - } - - public AVIMConversationQuery WhereDoesNotMatchesKeyInQuery(string key, string keyInQuery, AVQuery query) where TOther : AVObject { - condition.WhereDoesNotMatchesKeyInQuery(key, keyInQuery, query); - return this; - } - - public AVIMConversationQuery WhereMatchesQuery(string key, AVQuery query) where TOther : AVObject { - condition.WhereMatchesQuery(key, query); - return this; - } - - public AVIMConversationQuery WhereNear(string key, AVGeoPoint point) { - condition.WhereNear(key, point); - return this; - } - - public AVIMConversationQuery WhereNotContainedIn(string key, IEnumerable values) { - condition.WhereNotContainedIn(key, values); - return this; - } - - public AVIMConversationQuery WhereNotEqualTo(string key, object value) { - condition.WhereNotEqualTo(key, value); - return this; - } - - public AVIMConversationQuery WhereStartsWith(string key, string suffix) { - condition.WhereStartsWith(key, suffix); - return this; - } - - public AVIMConversationQuery WhereWithinGeoBox(string key, AVGeoPoint southwest, AVGeoPoint northeast) { - condition.WhereWithinGeoBox(key, southwest, northeast); - return this; - } - - public AVIMConversationQuery WhereWithinDistance(string key, AVGeoPoint point, AVGeoDistance maxDistance) { - condition.WhereWithinDistance(key, point, maxDistance); - return this; - } - - public AVIMConversationQuery WhereRelatedTo(AVObject parent, string key) { - condition.WhereRelatedTo(parent, key); - return this; - } - - #endregion - - public IDictionary BuildParameters(string className = null) { - return condition.BuildParameters(className); - } - - public IDictionary BuildWhere() { - return condition.ToJSON(); - } - } -} diff --git a/RTM/RTM/Public/AVIMEnumerator.cs b/RTM/RTM/Public/AVIMEnumerator.cs deleted file mode 100644 index d9368bc..0000000 --- a/RTM/RTM/Public/AVIMEnumerator.cs +++ /dev/null @@ -1,248 +0,0 @@ -using LeanCloud.Storage.Internal; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using LeanCloud.Realtime.Internal; - -namespace LeanCloud.Realtime -{ - - /// - /// AVIMM essage pager. - /// - public class AVIMMessagePager - { - /// - /// Gets the query. - /// - /// The query. - public AVIMMessageQuery Query { get; private set; } - - /// - /// Gets the size of the page. - /// - /// The size of the page. - public int PageSize - { - get - { - return Query.Limit; - } - - private set - { - Query.Limit = value; - } - } - - /// - /// Gets the current message identifier flag. - /// - /// The current message identifier flag. - public string CurrentMessageIdFlag - { - get - { - return Query.StartMessageId; - } - private set - { - Query.StartMessageId = value; - } - } - - /// - /// Gets the current date time flag. - /// - /// The current date time flag. - public DateTime CurrentDateTimeFlag - { - get - { - return Query.From; - } - private set - { - Query.From = value; - } - } - - internal AVIMMessagePager() - { - - } - - /// - /// Initializes a new instance of the class. - /// - /// Conversation. - public AVIMMessagePager(AVIMConversation conversation) - : this() - { - Query = conversation.GetMessageQuery(); - PageSize = 20; - CurrentDateTimeFlag = DateTime.Now; - } - - /// - /// Sets the size of the page. - /// - /// The page size. - /// Page size. - public AVIMMessagePager SetPageSize(int pageSize) - { - PageSize = pageSize; - return this; - } - - /// - /// Previouses the async. - /// - /// The async. - public Task> PreviousAsync() - { - return Query.FindAsync().OnSuccess(t => - { - var headerMessage = t.Result.FirstOrDefault(); - if (headerMessage != null) - { - CurrentMessageIdFlag = headerMessage.Id; - CurrentDateTimeFlag = headerMessage.ServerTimestamp.ToDateTime(); - } - return t.Result; - }); - } - - /// - /// from previous to lastest. - /// - /// - public Task> NextAsync() - { - return Query.ReverseFindAsync().OnSuccess(t => - { - var tailMessage = t.Result.LastOrDefault(); - if (tailMessage != null) - { - CurrentMessageIdFlag = tailMessage.Id; - CurrentDateTimeFlag = tailMessage.ServerTimestamp.ToDateTime(); - } - return t.Result; - }); - } - } - - /// - /// history message interator. - /// - public class AVIMMessageQuery - { - /// - /// Gets or sets the convsersation. - /// - /// The convsersation. - public AVIMConversation Convsersation { get; set; } - /// - /// Gets or sets the limit. - /// - /// The limit. - public int Limit { get; set; } - /// - /// Gets or sets from. - /// - /// From. - public DateTime From { get; set; } - /// - /// Gets or sets to. - /// - /// To. - public DateTime To { get; set; } - /// - /// Gets or sets the end message identifier. - /// - /// The end message identifier. - public string EndMessageId { get; set; } - /// - /// Gets or sets the start message identifier. - /// - /// The start message identifier. - public string StartMessageId { get; set; } - - - internal AVIMMessageQuery() - { - Limit = 20; - From = DateTime.Now; - } - - /// - /// Initializes a new instance of the class. - /// - /// Conversation. - public AVIMMessageQuery(AVIMConversation conversation) - : this() - { - Convsersation = conversation; - } - - /// - /// Sets the limit. - /// - /// The limit. - /// Limit. - public AVIMMessageQuery SetLimit(int limit) - { - Limit = limit; - return this; - } - - /// - /// from lastest to previous. - /// - /// - public Task> FindAsync() - { - return FindAsync(); - } - - /// - /// from lastest to previous. - /// - /// - public Task> ReverseFindAsync() - { - return ReverseFindAsync(); - } - - /// - /// Finds the async. - /// - /// The async. - /// set direction to reverse,it means query direct is from old to new. - /// The 1st type parameter. - public Task> FindAsync(bool reverse = false) - where T : IAVIMMessage - { - return Convsersation.QueryMessageAsync( - beforeTimeStampPoint: From, - afterTimeStampPoint: To, - limit: Limit, - afterMessageId: EndMessageId, - beforeMessageId: StartMessageId, - direction: reverse ? 0 : 1); - } - - /// - /// from previous to lastest. - /// - /// - public Task> ReverseFindAsync() - where T : IAVIMMessage - { - return FindAsync(true); - } - } -} diff --git a/RTM/RTM/Public/AVIMEventArgs.cs b/RTM/RTM/Public/AVIMEventArgs.cs deleted file mode 100644 index 2b91b23..0000000 --- a/RTM/RTM/Public/AVIMEventArgs.cs +++ /dev/null @@ -1,251 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; - -namespace LeanCloud.Realtime -{ - /// - /// - /// - public class AVIMEventArgs : EventArgs - { - public AVIMEventArgs() - { - - } - public AVIMException.ErrorCode ErrorCode { get; internal set; } - /// - /// LeanCloud 服务端发往客户端消息通知 - /// - public string Message { get; set; } - } - - public class AVIMDisconnectEventArgs : EventArgs - { - public int Code { get; private set; } - - public string Reason { get; private set; } - - public string Detail { get; private set; } - - public AVIMDisconnectEventArgs() - { - - } - public AVIMDisconnectEventArgs(int _code, string _reason, string _detail) - { - this.Code = _code; - this.Reason = _reason; - this.Detail = _detail; - } - } - - /// - /// 开始重连之后触发正在重连的事件通知,提供给监听者的事件参数 - /// - public class AVIMReconnectingEventArgs : EventArgs - { - /// - /// 是否由 SDK 内部机制启动的自动重连 - /// - public bool IsAuto { get; set; } - - /// - /// 重连的 client Id - /// - public string ClientId { get; set; } - - /// - /// 重连时使用的 SessionToken - /// - public string SessionToken { get; set; } - } - - /// - /// 重连成功之后的事件回调 - /// - public class AVIMReconnectedEventArgs : EventArgs - { - /// - /// 是否由 SDK 内部机制启动的自动重连 - /// - public bool IsAuto { get; set; } - - /// - /// 重连的 client Id - /// - public string ClientId { get; set; } - - /// - /// 重连时使用的 SessionToken - /// - public string SessionToken { get; set; } - } - - /// - /// 重连失败之后的事件回调参数 - /// - public class AVIMReconnectFailedArgs : EventArgs - { - /// - /// 是否由 SDK 内部机制启动的自动重连 - /// - public bool IsAuto { get; set; } - - /// - /// 重连的 client Id - /// - public string ClientId { get; set; } - - /// - /// 重连时使用的 SessionToken - /// - public string SessionToken { get; set; } - - /// - /// 失败的原因 - /// 0. 客户端网络断开 - /// 1. sessionToken 错误或者失效,需要重新创建 client - /// - public int FailedCode { get; set; } - } - - /// - /// AVIMM essage event arguments. - /// - public class AVIMMessageEventArgs : EventArgs - { - /// - /// Initializes a new instance of the class. - /// - /// I message. - public AVIMMessageEventArgs(IAVIMMessage iMessage) - { - Message = iMessage; - } - /// - /// Gets or sets the message. - /// - /// The message. - public IAVIMMessage Message { get; internal set; } - } - - /// - /// AVIMMessage event arguments. - /// - public class AVIMMessagePatchEventArgs : EventArgs - { - public AVIMMessagePatchEventArgs(IAVIMMessage message) - { - Message = message; - } - - /// - /// Gets or sets the message. - /// - /// The message. - public IAVIMMessage Message { get; internal set; } - } - - /// - /// AVIMT ext message event arguments. - /// - public class AVIMTextMessageEventArgs : EventArgs - { - /// - /// Initializes a new instance of the class. - /// - /// Raw. - public AVIMTextMessageEventArgs(AVIMTextMessage raw) - { - TextMessage = raw; - } - /// - /// Gets or sets the text message. - /// - /// The text message. - public AVIMTextMessage TextMessage { get; internal set; } - } - - /// - /// 当对话中有人加入时,触发 时所携带的事件参数 - /// - public class AVIMOnMembersJoinedEventArgs : EventArgs - { - /// - /// 加入到对话的 Client Id(s) - /// - public IEnumerable JoinedMembers { get; internal set; } - - /// - /// 邀请的操作人 - /// - public string InvitedBy { get; internal set; } - - /// - /// 此次操作针对的对话 Id - /// - public string ConversationId { get; internal set; } - } - - /// - /// 当对话中有人加入时,触发 AVIMMembersJoinListener 时所携带的事件参数 - /// - public class AVIMOnMembersLeftEventArgs : EventArgs - { - /// - /// 离开对话的 Client Id(s) - /// - public IEnumerable LeftMembers { get; internal set; } - - /// - /// 踢出的操作人 - /// - public string KickedBy { get; internal set; } - - /// - /// 此次操作针对的对话 Id - /// - public string ConversationId { get; internal set; } - } - /// - /// 当前用户被邀请加入到对话 - /// - public class AVIMOnInvitedEventArgs : EventArgs - { - /// - /// 邀请的操作人 - /// - public string InvitedBy { get; internal set; } - - /// - /// 此次操作针对的对话 Id - /// - public string ConversationId { get; internal set; } - } - - /// - /// 当前用户被他人从对话中踢出 - /// - public class AVIMOnKickedEventArgs : EventArgs - { - /// - /// 踢出的操作人 - /// - public string KickedBy { get; internal set; } - - /// - /// 此次操作针对的对话 Id - /// - public string ConversationId { get; internal set; } - } - - public class AVIMSessionClosedEventArgs : EventArgs - { - public int Code { get; internal set; } - - public string Reason { get; internal set; } - - public string Detail { get; internal set; } - } -} diff --git a/RTM/RTM/Public/AVIMException.cs b/RTM/RTM/Public/AVIMException.cs deleted file mode 100644 index f473746..0000000 --- a/RTM/RTM/Public/AVIMException.cs +++ /dev/null @@ -1,235 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud.Realtime -{ - /// - /// 实时通信的异常 - /// - public class AVIMException : Exception - { - /// - /// 错误代码 - /// - public enum ErrorCode - { - /// - /// Error code indicating that an unknown error or an error unrelated to LeanCloud - /// occurred. - /// - OtherCause = -1, - - /// - /// 服务端错误 - /// - FromServer = 4000, - - - /// - /// websocket 连接非正常关闭,通常见于路由器配置对长连接限制的情况。SDK 会自动重连,无需人工干预。 - /// - UnknownError = 1006, - - /// - /// 应用不存在或应用禁用了实时通信服务 - /// - APP_NOT_AVAILABLE = 4100, - - - /// - /// 登录签名验证失败 - /// - SIGNATURE_FAILED = 4102, - - /// - /// Client ClientId 格式错误,超过 64 个字符。 - /// - INVALID_LOGIN = 4103, - - /// - /// Session 没有打开就发送消息,或执行其他操作。常见的错误场景是调用 open session 后直接发送消息,正确的用法是在 Session 打开的回调里执行。 - /// - SESSION_REQUIRED = 4105, - - /// - /// 读超时,服务器端长时间没有收到客户端的数据,切断连接。SDK 包装了心跳包的机制,出现此错误通常是网络问题。SDK 会自动重连,无需人工干预。 - /// - READ_TIMEOUT = 4107, - - /// - /// 登录超时,连接后长时间没有完成 session open。通常是登录被拒绝等原因,出现此问题可能是使用方式有误,可以 创建工单,由我们技术顾问来给出建议。 - /// - LOGIN_TIMEOUT = 4108, - - /// - /// 包过长。消息大小超过 5 KB,请缩短消息或者拆分消息。 - /// - FRAME_TOO_LONG = 4109, - - /// - /// 设置安全域名后,当前登录的域名与安全域名不符合。 - /// - INVALID_ORIGIN = 4110, - - - /// - /// 设置单设备登录后,客户端被其他设备挤下线。 - /// - SESSION_CONFLICT = 4111, - - /// - /// 应用容量超限,当天登录用户数已经超过应用设定的最大值。 - /// - APP_QUOTA_EXCEEDED = 4113, - - /// - /// 客户端发送的序列化数据服务器端无法解析。 - /// - UNPARSEABLE_RAW_MESSAGE = 4114, - - /// - /// 客户端被 REST API 管理接口强制下线。 - /// - KICKED_BY_APP = 4115, - - /// - /// 应用单位时间内发送消息量超过限制,消息被拒绝。 - /// - MESSAGE_SENT_QUOTA_EXCEEDED = 4116, - - /// - /// 服务器内部错误,如果反复出现请收集相关线索并 创建工单,我们会尽快解决。 - /// - INTERNAL_ERROR = 4200, - - /// - /// 通过 API 发送消息超时 - /// - SEND_MESSAGE_TIMEOUT = 4201, - - /// - /// 上游 API 调用异常,请查看报错信息了解错误详情 - /// - CONVERSATION_API_FAILED = 4301, - - /// - /// 对话相关操作签名错误 - /// - CONVERSATION_SIGNATURE_FAILED = 4302, - - /// - /// 发送消息,或邀请等操作对应的对话不存在。 - /// - CONVERSATION_NOT_FOUND = 4303, - - /// - /// 对话成员已满,不能再添加。 - /// - CONVERSATION_FULL = 4304, - - /// - /// 对话操作被应用的云引擎 Hook 拒绝 - /// - CONVERSATION_REJECTED_BY_APP = 4305, - - /// - /// 更新对话操作失败 - /// - CONVERSATION_UPDATE_FAILED = 4306, - - /// - /// 该对话为只读,不能更新或增删成员。 - /// - CONVERSATION_READ_ONLY = 4307, - - /// - /// 该对话禁止当前用户发送消息 - /// - CONVERSATION_NOT_ALLOWED = 4308, - - /// - /// 更新对话的请求被拒绝,当前用户不在对话中 - /// - CONVERSATION_UPDATE_REJECT = 4309, - - /// - /// 查询对话失败,常见于慢查询导致的超时或受其他慢查询导致的数据库响应慢 - /// - CONVERSATION_QUERY_FAILED = 4310, - - /// - /// 拉取对话消息记录失败,常见与超时的情况 - /// - CONVERSATION_LOG_FAILED = 4311, - - /// - /// 拉去对话消息记录被拒绝,当前用户不再对话中 - /// - CONVERSATION_LOG_REJECT = 4312, - - /// - /// 该功能仅对系统对话有效 - /// - SYSTEM_CONVERSATION_REQUIRED = 4313, - - - /// - /// 该功能仅对普通对话有效。 - /// - NORMAL_CONVERSATION_REQUIRED = 4314, - - - /// - /// 当前用户被加入此对话的黑名单,无法发送消息。 - /// - CONVERSATION_BLACKLISTED = 4315, - - - /// - /// 该功能仅对暂态对话有效。 - /// - TRANSIENT_CONVERSATION_REQUIRED = 4316, - - /// - /// 发送消息的对话不存在,或当前用户不在对话中 - /// - INVALID_MESSAGING_TARGET = 4401, - - /// - /// 发送的消息被应用的云引擎 Hook 拒绝 - /// - MESSAGE_REJECTED_BY_APP = 4402, - - /// - /// 客户端无法通过 WebSocket 发送数据包 - /// - CAN_NOT_EXCUTE_COMMAND = 1002, - - } - /// - /// 用户云代码返回的错误码 - /// - public int AppCode { get; private set; } - - - internal AVIMException(ErrorCode code, string message, Exception cause = null) - : base(message, cause) - { - this.Code = code; - } - - internal AVIMException(int code, int appCode, string message, Exception cause = null) - : this((ErrorCode)code, message, cause) - { - this.AppCode = appCode; - } - - /// - /// The LeanCloud error code associated with the exception. - /// - public ErrorCode Code { get; private set; } - } -} diff --git a/RTM/RTM/Public/AVIMImageMessage.cs b/RTM/RTM/Public/AVIMImageMessage.cs deleted file mode 100644 index 9e00b67..0000000 --- a/RTM/RTM/Public/AVIMImageMessage.cs +++ /dev/null @@ -1,246 +0,0 @@ -using LeanCloud; -using LeanCloud.Storage.Internal; -using LeanCloud.Realtime.Internal; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.IO; - -namespace LeanCloud.Realtime -{ - /// - /// 图像消息 - /// - [AVIMMessageClassName("_AVIMImageMessage")] - [AVIMTypedMessageTypeInt(-2)] - public class AVIMImageMessage : AVIMFileMessage - { - - } - - /// - /// File message. - /// - [AVIMMessageClassName("_AVIMFileMessage")] - [AVIMTypedMessageTypeInt(-6)] - public class AVIMFileMessage : AVIMMessageDecorator - { - /// - /// Initializes a new instance of the class. - /// - public AVIMFileMessage() - : base(new AVIMTypedMessage()) - { - - } - - /// - /// Initializes a new instance of the class. - /// - /// Message. - public AVIMFileMessage(AVIMTypedMessage message) - : base(message) - { - - } - - /// - /// Gets or sets the file. - /// - /// The file. - public AVFile File { get; set; } - - /// - /// Froms the URL. - /// - /// The URL. - /// URL. - /// The 1st type parameter. - public static T FromUrl(string url) where T : AVIMFileMessage, new() - { - return FromUrl(string.Empty.Random(8), url, null); - } - - /// - /// From the URL. - /// - /// The URL. - /// File name. - /// External URL. - /// Text title. - /// Custom attributes. - /// The 1st type parameter. - public static T FromUrl(string fileName, string externalUrl, string textTitle, IDictionary customAttributes = null) where T : AVIMFileMessage, new() - { - T message = new T(); - message.File = new AVFile(fileName, externalUrl); - message.TextContent = textTitle; - message.MergeCustomAttributes(customAttributes); - return message; - } - - /// - /// From the stream. - /// - /// The stream. - /// File name. - /// Data. - /// MIME type. - /// Text title. - /// Meta data. - /// Custom attributes. - /// The 1st type parameter. - public static T FromStream(string fileName, Stream data, string mimeType, string textTitle, IDictionary metaData, IDictionary customAttributes = null) where T : AVIMFileMessage, new() - { - T message = new T(); - message.File = new AVFile(fileName, data, mimeType, metaData); - message.TextContent = textTitle; - message.MergeCustomAttributes(customAttributes); - return message; - } - - /// - /// Encodes the decorator. - /// - /// The decorator. - public override IDictionary EncodeDecorator() - { - if (File.Url == null) throw new InvalidOperationException("File.Url can not be null before it can be sent."); - File.MetaData["name"] = File.Name; - File.MetaData["format"] = File.MimeType; - var fileData = new Dictionary - { - { "url", File.Url.ToString()}, - { "objId", File.ObjectId }, - { "metaData", File.MetaData } - }; - - return new Dictionary - { - { AVIMProtocol.LCFILE, fileData } - }; - } - - /// - /// Deserialize the specified msgStr. - /// - /// The deserialize. - /// Message string. - public override IAVIMMessage Deserialize(string msgStr) - { - var msg = Json.Parse(msgStr) as IDictionary; - var fileData = msg[AVIMProtocol.LCFILE] as IDictionary; - string mimeType = null; - string url = null; - string name = null; - string objId = null; - IDictionary metaData = null; - if (fileData != null) - { - object metaDataObj = null; - - if (fileData.TryGetValue("metaData", out metaDataObj)) - { - metaData = metaDataObj as IDictionary; - object fileNameObj = null; - if (metaData != null) - { - if (metaData.TryGetValue("name", out fileNameObj)) - { - name = fileNameObj.ToString(); - } - } - object mimeTypeObj = null; - if (metaData != null) - { - if (metaData.TryGetValue("format", out mimeTypeObj)) - { - if (mimeTypeObj != null) - mimeType = mimeTypeObj.ToString(); - } - } - } - - object objIdObj = null; - if (fileData.TryGetValue("objId", out objIdObj)) - { - if (objIdObj != null) - objId = objIdObj.ToString(); - } - - object urlObj = null; - if (fileData.TryGetValue("url", out urlObj)) - { - url = urlObj.ToString(); - } - File = AVFile.CreateWithData(objId, name, url, metaData); - } - - return base.Deserialize(msgStr); - } - } - - /// - /// Location message. - /// - [AVIMMessageClassName("_AVIMMessageClassName")] - [AVIMTypedMessageTypeInt(-5)] - public class AVIMLocationMessage : AVIMMessageDecorator - { - /// - /// Initializes a new instance of the class. - /// - public AVIMLocationMessage() - : base(new AVIMTypedMessage()) - { - - } - - /// - /// Gets or sets the location. - /// - /// The location. - public AVGeoPoint Location { get; set; } - - public AVIMLocationMessage(AVGeoPoint location) - : this() - { - Location = location; - } - - /// - /// Encodes the decorator. - /// - /// The decorator. - public override IDictionary EncodeDecorator() - { - var locationData = new Dictionary - { - { "longitude", Location.Longitude}, - { "latitude", Location.Latitude } - }; - return new Dictionary - { - { AVIMProtocol.LCLOC, locationData } - }; - } - - /// - /// Deserialize the specified msgStr. - /// - /// The deserialize. - /// Message string. - public override IAVIMMessage Deserialize(string msgStr) - { - var msg = Json.Parse(msgStr) as IDictionary; - var locationData = msg[AVIMProtocol.LCLOC] as IDictionary; - if (locationData != null) - { - Location = new AVGeoPoint(double.Parse(locationData["latitude"].ToString()), double.Parse(locationData["longitude"].ToString())); - } - return base.Deserialize(msgStr); - } - } -} diff --git a/RTM/RTM/Public/AVIMMessage.cs b/RTM/RTM/Public/AVIMMessage.cs deleted file mode 100644 index 21fb38d..0000000 --- a/RTM/RTM/Public/AVIMMessage.cs +++ /dev/null @@ -1,162 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using LeanCloud; -using System.Reflection; -using LeanCloud.Storage.Internal; -using System.Threading; -using System.Collections; -using LeanCloud.Realtime.Internal; - -namespace LeanCloud.Realtime -{ - /// - /// 实时消息的核心基类,它是 Json schema 消息的父类 - /// - [AVIMMessageClassName("_AVIMMessage")] - public class AVIMMessage : IAVIMMessage - { - /// - /// 默认的构造函数 - /// - public AVIMMessage() - { - - } - internal readonly object mutex = new object(); - - /// - /// 对话的Id - /// - public string ConversationId { get; set; } - - /// - /// 发送消息的 ClientId - /// - public string FromClientId { get; set; } - - /// - /// 消息在全局的唯一标识Id - /// - public string Id { get; set; } - - /// - /// 服务器端的时间戳 - /// - public long ServerTimestamp { get; set; } - - /// - /// Gets or sets the content. - /// - /// The content. - public string Content { get; set; } - - /// - /// 对方收到消息的时间戳,如果是多人聊天,那以最早收到消息的人回发的 ACK 为准 - /// - public long RcpTimestamp { get; set; } - - public long UpdatedAt { get; set; } - - internal string cmdId { get; set; } - - #region - /// - /// Gets or sets a value indicating whether this mention all. - /// - /// true if mention all; otherwise, false. - public bool MentionAll { get; set; } - - /// - /// Gets or sets the mention list. - /// - /// The mention list. - public IEnumerable MentionList { get; set; } - - #endregion - - #region register convertor for custom typed message - - /// - /// Serialize this message. - /// - /// The serialize. - public virtual string Serialize() - { - return Content; - } - - /// - /// Validate the specified msgStr. - /// - /// The validate. - /// Message string. - public virtual bool Validate(string msgStr) - { - return true; - } - - /// - /// Deserialize the specified msgStr to message subclass instance - /// - /// The deserialize. - /// Message string. - public virtual IAVIMMessage Deserialize(string msgStr) - { - Content = msgStr; - return this; - } - - internal virtual MessageCommand BeforeSend(MessageCommand cmd) - { - return cmd; - } - - internal static IAVIMMessage CopyMetaData(IAVIMMessage srcMsg, IAVIMMessage desMsg) { - if (srcMsg == null) - return desMsg; - - desMsg.ConversationId = srcMsg.ConversationId; - desMsg.FromClientId = srcMsg.FromClientId; - desMsg.Id = srcMsg.Id; - desMsg.ServerTimestamp = srcMsg.ServerTimestamp; - desMsg.RcpTimestamp = srcMsg.RcpTimestamp; - desMsg.UpdatedAt = srcMsg.UpdatedAt; - return desMsg; - } - - #endregion - } - - - /// - /// 消息的发送选项 - /// - public struct AVIMSendOptions - { - /// - /// 是否需要送达回执 - /// - public bool Receipt; - /// - /// 是否是暂态消息,暂态消息不返回送达回执(ack),不保留离线消息,不触发离线推送 - /// - public bool Transient; - /// - /// 消息的优先级,默认是1,可选值还有 2|3 - /// - public int Priority; - /// - /// 是否为 Will 类型的消息,这条消息会被缓存在服务端,一旦当前客户端下线,这条消息会被发送到对话内的其他成员 - /// - public bool Will; - - /// - /// 如果消息的接收者已经下线了,这个字段的内容就会被离线推送到接收者 - ///例如,一张图片消息的离线消息内容可以类似于:[您收到一条图片消息,点击查看] 这样的推送内容,参照微信的做法 - /// - public IDictionary PushData; - } -} diff --git a/RTM/RTM/Public/AVIMMessageClassNameAttribute.cs b/RTM/RTM/Public/AVIMMessageClassNameAttribute.cs deleted file mode 100644 index e4d7228..0000000 --- a/RTM/RTM/Public/AVIMMessageClassNameAttribute.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud.Realtime -{ - [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)] - public sealed class AVIMMessageClassNameAttribute: Attribute - { - public AVIMMessageClassNameAttribute(string className) - { - this.ClassName = className; - } - public string ClassName { get; private set; } - - } -} diff --git a/RTM/RTM/Public/AVIMMessageFieldNameAttribute.cs b/RTM/RTM/Public/AVIMMessageFieldNameAttribute.cs deleted file mode 100644 index 0e155b5..0000000 --- a/RTM/RTM/Public/AVIMMessageFieldNameAttribute.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace LeanCloud.Realtime -{ - [AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)] - public sealed class AVIMMessageFieldNameAttribute: Attribute - { - public AVIMMessageFieldNameAttribute(string fieldName) - { - FieldName = fieldName; - } - - public string FieldName { get; private set; } - } -} diff --git a/RTM/RTM/Public/AVIMMessageListener.cs b/RTM/RTM/Public/AVIMMessageListener.cs deleted file mode 100644 index d0d91cf..0000000 --- a/RTM/RTM/Public/AVIMMessageListener.cs +++ /dev/null @@ -1,143 +0,0 @@ -using LeanCloud.Realtime.Internal; -using LeanCloud.Storage.Internal; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud.Realtime -{ - /// - /// 默认的消息监听器,它主要承担的指责是回执的发送与用户自定义的监听器不冲突 - /// - public class AVIMMessageListener : IAVIMListener - { - /// - /// 默认的 AVIMMessageListener 只会监听 direct 协议,但是并不会触发针对消息类型的判断的监听器 - /// - public AVIMMessageListener() - { - - } - - /// - /// Protocols the hook. - /// - /// true, if hook was protocoled, false otherwise. - /// Notice. - public virtual bool ProtocolHook(AVIMNotice notice) - { - if (notice.CommandName != "direct") return false; - if (notice.RawData.ContainsKey("offline")) return false; - return true; - } - - private EventHandler m_OnMessageReceived; - /// - /// 接收到聊天消息的事件通知 - /// - public event EventHandler OnMessageReceived - { - add - { - m_OnMessageReceived += value; - } - remove - { - m_OnMessageReceived -= value; - } - } - internal virtual void OnMessage(AVIMNotice notice) - { - if (m_OnMessageReceived != null) - { - var msgStr = notice.RawData["msg"].ToString(); - var iMessage = AVRealtime.FreeStyleMessageClassingController.Instantiate(msgStr, notice.RawData); - //var messageNotice = new AVIMMessageNotice(notice.RawData); - //var messaegObj = AVIMMessage.Create(messageNotice); - var args = new AVIMMessageEventArgs(iMessage); - m_OnMessageReceived.Invoke(this, args); - } - } - - /// - /// Ons the notice received. - /// - /// Notice. - public virtual void OnNoticeReceived(AVIMNotice notice) - { - this.OnMessage(notice); - } - - } - - /// - /// 文本消息监听器 - /// - public class AVIMTextMessageListener : IAVIMListener - { - /// - /// 构建默认的文本消息监听器 - /// - public AVIMTextMessageListener() - { - - } - - /// - /// 构建文本消息监听者 - /// - /// - public AVIMTextMessageListener(Action textMessageReceived) - { - OnTextMessageReceived += (sender, textMessage) => - { - textMessageReceived(textMessage.TextMessage); - }; - } - - private EventHandler m_OnTextMessageReceived; - public event EventHandler OnTextMessageReceived - { - add - { - m_OnTextMessageReceived += value; - } - remove - { - m_OnTextMessageReceived -= value; - } - } - - public virtual bool ProtocolHook(AVIMNotice notice) - { - if (notice.CommandName != "direct") return false; - try - { - var msg = Json.Parse(notice.RawData["msg"].ToString()) as IDictionary; - if (!msg.Keys.Contains(AVIMProtocol.LCTYPE)) return false; - var typInt = 0; - int.TryParse(msg[AVIMProtocol.LCTYPE].ToString(), out typInt); - if (typInt != -1) return false; - return true; - } - catch(ArgumentException) - { - - } - return false; - - } - - public virtual void OnNoticeReceived(AVIMNotice notice) - { - if (m_OnTextMessageReceived != null) - { - var textMessage = new AVIMTextMessage(); - textMessage.Deserialize(notice.RawData["msg"].ToString()); - m_OnTextMessageReceived(this, new AVIMTextMessageEventArgs(textMessage)); - } - } - } -} diff --git a/RTM/RTM/Public/AVIMNotice.cs b/RTM/RTM/Public/AVIMNotice.cs deleted file mode 100644 index 65f4053..0000000 --- a/RTM/RTM/Public/AVIMNotice.cs +++ /dev/null @@ -1,57 +0,0 @@ -using LeanCloud; -using LeanCloud.Realtime.Internal; -using LeanCloud.Storage.Internal; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud.Realtime -{ - /// - /// - /// - public interface IAVIMNotice - { - /// - /// - /// - /// - /// - AVIMNotice Restore(IDictionary estimatedData); - } - /// - /// 从服务端接受到的通知 - /// 通知泛指消息,对话信息变更(例如加人和被踢等),服务器的 ACK,消息回执等 - /// - public class AVIMNotice : EventArgs - { - /// - /// Initializes a new instance of the class. - /// - public AVIMNotice() - { - - } - - /// - /// The name of the command. - /// - public readonly string CommandName; - public readonly IDictionary RawData; - public AVIMNotice(IDictionary estimatedData) - { - this.RawData = estimatedData; - this.CommandName = estimatedData["cmd"].ToString(); - } - - public static bool IsValidLeanCloudProtocol(IDictionary estimatedData) - { - if (estimatedData == null) return false; - if (estimatedData.Count == 0) return false; - return true; - } - } -} diff --git a/RTM/RTM/Public/AVIMRecalledMessage.cs b/RTM/RTM/Public/AVIMRecalledMessage.cs deleted file mode 100644 index 1e863fd..0000000 --- a/RTM/RTM/Public/AVIMRecalledMessage.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace LeanCloud.Realtime { - /// - /// 撤回消息 - /// - [AVIMMessageClassName("_AVIMRecalledMessagee")] - [AVIMTypedMessageTypeInt(-127)] - public class AVIMRecalledMessage : AVIMTypedMessage { - - } -} diff --git a/RTM/RTM/Public/AVIMSignature.cs b/RTM/RTM/Public/AVIMSignature.cs deleted file mode 100644 index c5bd0d3..0000000 --- a/RTM/RTM/Public/AVIMSignature.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud.Realtime -{ - /// - /// 签名 - /// - public class AVIMSignature - { - /// - /// 经过 SHA1 以及相关操作参数计算出来的加密字符串 - /// - public string SignatureContent { get; set; } - - /// - /// 服务端时间戳 - /// - public long Timestamp { get; set; } - - /// - /// 随机字符串 - /// - public string Nonce { get; set; } - - /// - /// 构造一个签名 - /// - /// - /// - /// - public AVIMSignature(string s,long t,string n) - { - this.Nonce = n; - this.SignatureContent = s; - this.Timestamp = t; - } - } -} diff --git a/RTM/RTM/Public/AVIMTemporaryConversation.cs b/RTM/RTM/Public/AVIMTemporaryConversation.cs deleted file mode 100644 index 1e06769..0000000 --- a/RTM/RTM/Public/AVIMTemporaryConversation.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud.Realtime -{ - /// - /// Temporary conversation. - /// - public class AVIMTemporaryConversation : AVIMConversation - { - public DateTime ExpiredAt - { - get - { - if (expiredAt == null) - return DateTime.Now.AddDays(1); - return expiredAt.Value; - } - - set - { - expiredAt = value; - } - } - - internal AVIMTemporaryConversation(long ttl) - : base(isTemporary: true) - { - this.expiredAt = DateTime.Now.AddDays(1); - } - } - - -} diff --git a/RTM/RTM/Public/AVIMTextMessage.cs b/RTM/RTM/Public/AVIMTextMessage.cs deleted file mode 100644 index 7bc1d34..0000000 --- a/RTM/RTM/Public/AVIMTextMessage.cs +++ /dev/null @@ -1,47 +0,0 @@ -using LeanCloud.Realtime.Internal; -using LeanCloud.Storage.Internal; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud.Realtime -{ - /// - /// 纯文本信息 - /// - [AVIMMessageClassName("_AVIMTextMessage")] - [AVIMTypedMessageTypeInt(-1)] - public class AVIMTextMessage : AVIMTypedMessage - { - /// - /// 构建一个文本信息 class. - /// - public AVIMTextMessage() - { - - } - - /// - /// 文本类型标记 - /// - [Obsolete("LCType is deprecated, please use AVIMTypedMessageTypeInt instead.")] - [AVIMMessageFieldName("_lctype")] - public int LCType - { - get; set; - } - - /// - /// 构造一个纯文本信息 - /// - /// - public AVIMTextMessage(string textContent) - : this() - { - TextContent = textContent; - } - } -} diff --git a/RTM/RTM/Public/AVIMTypedMessage.cs b/RTM/RTM/Public/AVIMTypedMessage.cs deleted file mode 100644 index c756e1f..0000000 --- a/RTM/RTM/Public/AVIMTypedMessage.cs +++ /dev/null @@ -1,205 +0,0 @@ -using LeanCloud.Storage.Internal; -using LeanCloud.Realtime.Internal; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace LeanCloud.Realtime -{ - /// - /// - /// - [AVIMMessageClassName("_AVIMTypedMessage")] - [AVIMTypedMessageTypeInt(0)] - public class AVIMTypedMessage : AVIMMessage, IEnumerable> - { - /// - /// Initializes a new instance of the class. - /// - public AVIMTypedMessage() - { - - } - - /// - /// 文本内容 - /// - [AVIMMessageFieldName("_lctext")] - public string TextContent - { - get; set; - } - - private IDictionary estimatedData = new Dictionary(); - /// - /// Serialize this instance. - /// - /// The serialize. - public override string Serialize() - { - var result = Encode(); - var resultStr = Json.Encode(result); - this.Content = resultStr; - return resultStr; - } - - /// - /// Encode this instance. - /// - /// The encode. - public virtual IDictionary Encode() - { - var result = AVRealtime.FreeStyleMessageClassingController.EncodeProperties(this); - var encodedAttrs = PointerOrLocalIdEncoder.Instance.Encode(estimatedData); - result[AVIMProtocol.LCATTRS] = estimatedData; - return result; - } - - /// - /// Validate the specified msgStr. - /// - /// The validate. - /// Message string. - public override bool Validate(string msgStr) - { - try - { - var msg = Json.Parse(msgStr) as IDictionary; - return msg.ContainsKey(AVIMProtocol.LCTYPE); - } - catch - { - - } - return false; - } - - /// - /// Deserialize the specified msgStr. - /// - /// The deserialize. - /// Message string. - public override IAVIMMessage Deserialize(string msgStr) - { - var msg = Json.Parse(msgStr) as IDictionary; - var className = AVRealtime.FreeStyleMessageClassingController.GetClassName(this.GetType()); - var PropertyMappings = AVRealtime.FreeStyleMessageClassingController.GetPropertyMappings(className); - var messageFieldProperties = PropertyMappings.Where(prop => msg.ContainsKey(prop.Value)) - .Select(prop => Tuple.Create(ReflectionHelpers.GetProperty(this.GetType(), prop.Key), msg[prop.Value])); - - foreach (var property in messageFieldProperties) - { - property.Item1.SetValue(this, property.Item2, null); - } - - if (msg.ContainsKey(AVIMProtocol.LCATTRS)) - { - object attrs = msg[AVIMProtocol.LCATTRS]; - this.estimatedData = AVDecoder.Instance.Decode(attrs) as Dictionary; - } - - return base.Deserialize(msgStr); - } - - /// - /// Gets or sets the with the specified key. - /// - /// Key. - public virtual object this[string key] - { - get - { - if (estimatedData.TryGetValue(key, out object value)) { - return value; - } - return null; - } - set - { - estimatedData[key] = value; - } - } - - /// - /// Merges the custom attributes. - /// - /// Custom attributes. - public void MergeCustomAttributes(IDictionary customAttributes) - { - this.estimatedData = this.estimatedData.Merge(customAttributes); - } - - /// - /// Gets the enumerator. - /// - /// The enumerator. - public IEnumerator> GetEnumerator() - { - return estimatedData.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return ((IEnumerable>)this).GetEnumerator(); - } - } - - /// - /// AVIMMessage decorator. - /// - public abstract class AVIMMessageDecorator : AVIMTypedMessage - { - /// - /// Gets or sets the message. - /// - /// The message. - public AVIMTypedMessage Message { get; set; } - - /// - /// Initializes a new instance of the class. - /// - /// Message. - protected AVIMMessageDecorator(AVIMTypedMessage message) - { - this.Message = message; - } - - /// - /// Gets or sets the content of the message. - /// - /// The content of the message. - public virtual IDictionary MessageContent { get; set; } - - /// - /// Encodes the decorated. - /// - /// The decorated. - public virtual IDictionary EncodeDecorated() - { - return Message.Encode(); - } - - /// - /// Encode this instance. - /// - /// The encode. - public override IDictionary Encode() - { - var decoratedMessageEncoded = EncodeDecorated(); - var selfEncoded = base.Encode(); - var decoratoEncoded = this.EncodeDecorator(); - var resultEncoed = decoratedMessageEncoded.Merge(selfEncoded).Merge(decoratoEncoded); - return resultEncoed; - } - - /// - /// Encodes the decorator. - /// - /// The decorator. - public abstract IDictionary EncodeDecorator(); - } - - -} diff --git a/RTM/RTM/Public/AVIMTypedMessageTypeIntAttribute.cs b/RTM/RTM/Public/AVIMTypedMessageTypeIntAttribute.cs deleted file mode 100644 index 49c4b76..0000000 --- a/RTM/RTM/Public/AVIMTypedMessageTypeIntAttribute.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -namespace LeanCloud.Realtime -{ - [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)] - public sealed class AVIMTypedMessageTypeIntAttribute : Attribute - { - public AVIMTypedMessageTypeIntAttribute(int typeInt) - { - this.TypeInteger = typeInt; - } - - public int TypeInteger { get; private set; } - } -} diff --git a/RTM/RTM/Public/AVRealtime.cs b/RTM/RTM/Public/AVRealtime.cs deleted file mode 100644 index 49bd9ec..0000000 --- a/RTM/RTM/Public/AVRealtime.cs +++ /dev/null @@ -1,1282 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Text; -using System.Threading.Tasks; -using LeanCloud; -using System.Reflection; -using LeanCloud.Realtime.Internal; -using LeanCloud.Storage.Internal; -using System.Threading; - -#if UNITY -using UnityEngine; -#endif -namespace LeanCloud.Realtime -{ - - /// - /// 实时消息的框架类 - /// 包含了 WebSocket 连接以及事件通知的管理 - /// - public class AVRealtime - { - internal static IDictionary clients = null; - - private static readonly object mutex = new object(); - private string _wss; - private string _secondaryWss; - private string _sesstionToken; - private long _sesstionTokenExpire; - private string _clientId; - private string _deviceId; - private bool _secure; - private string _tag; - private string subprotocolPrefix = "lc.json."; - - static readonly int RECONNECT_DELAY = 5 * 1000; - static readonly int RECONNECT_USE_SECONDARY_TIMES = 6; - static readonly int RECONNECT_FROM_APP_ROUTER = 12; - - int reconnectTimes; - - public bool IsSesstionTokenExpired - { - get - { - return DateTime.Now.ToUnixTimeStamp() > _sesstionTokenExpire; - } - } - - - - private IAVIMCommandRunner avIMCommandRunner; - - - /// - /// - /// - public IAVIMCommandRunner AVIMCommandRunner - { - get - { - lock (mutex) - { - avIMCommandRunner = avIMCommandRunner ?? new AVIMCommandRunner(this.AVWebSocketClient); - return avIMCommandRunner; - } - } - } - - private IWebSocketClient webSocketController; - internal IWebSocketClient AVWebSocketClient - { - get - { - lock (mutex) - { - webSocketController = webSocketController ?? new DefaultWebSocketClient(); - return webSocketController; - } - } - set - { - lock (mutex) - { - webSocketController = value; - } - } - } - - internal static IAVRouterController RouterController - { - get - { - return AVIMCorePlugins.Instance.RouterController; - } - } - - internal static IFreeStyleMessageClassingController FreeStyleMessageClassingController - { - get - { - return AVIMCorePlugins.Instance.FreeStyleClassingController; - } - } - - /// - /// - /// - public event EventHandler OnOfflineMessageReceived; - - /// - /// 与云端通讯的状态 - /// - public enum Status - { - /// - /// 未初始化 - /// - None = -1, - - /// - /// 正在连接 - /// - Connecting = 0, - - /// - /// 已连接 - /// - Online = 1, - - /// - /// 连接已断开 - /// - Offline = 2, - - /// - /// 正在重连 - /// - Reconnecting = 3, - - /// - /// websocket 连接已被打开 - /// - Opened = 98, - - /// - /// 已主动关闭 - /// - Closed = 99, - } - - private AVRealtime.Status state = Status.None; - public AVRealtime.Status State - { - get - { - return state; - } - private set - { - state = value; - } - } - - private struct NetworkStateOptions - { - public bool Available { get; set; } - } - - private NetworkStateOptions NetworkState { get; set; } - - private struct WebSocketStateOptions - { - public int ClosedCode { get; set; } - } - - private WebSocketStateOptions WebSocketState { get; set; } - - /// - /// - /// - public struct AVIMReconnectOptions - { - /// - /// 重连的时间间隔,单位是秒 - /// - public long Interval { get; set; } - - /// - /// 重连的次数 - /// - public int Retry { get; set; } - } - - internal string Subprotocol - { - get - { - return subprotocolPrefix + (int)CurrentConfiguration.OfflineMessageStrategy; - } - } - - /// - /// 重连选项 - /// - public AVIMReconnectOptions ReconnectOptions { get; set; } - - private ISignatureFactory _signatureFactory; - - /// - /// 签名接口 - /// - public ISignatureFactory SignatureFactory - { - get - { - _signatureFactory = _signatureFactory ?? new DefaulSiganatureFactory(); - return _signatureFactory; - } - set - { - _signatureFactory = value; - } - } - - private bool useLeanEngineSignaturFactory; - /// - /// 启用 LeanEngine 云函数签名 - /// - public void UseLeanEngineSignatureFactory() - { - useLeanEngineSignaturFactory = true; - this.SignatureFactory = new LeanEngineSignatureFactory(); - } - - private EventHandler m_OnDisconnected; - /// - /// 连接断开触发的事件 - /// 如果其他客户端使用了相同的 Tag 登录,就会导致当前用户被服务端断开 - /// - public event EventHandler OnDisconnected - { - add - { - m_OnDisconnected += value; - } - remove - { - m_OnDisconnected -= value; - } - } - - private EventHandler m_OnReconnecting; - /// - /// 正在重连时触发的事件 - /// - public event EventHandler OnReconnecting - { - add - { - m_OnReconnecting += value; - } - remove - { - m_OnReconnecting -= value; - } - } - - private EventHandler m_OnReconnected; - /// - /// 重连之后触发的事件 - /// - public event EventHandler OnReconnected - { - add - { - m_OnReconnected += value; - } - remove - { - m_OnReconnected -= value; - } - } - - private EventHandler m_OnReconnectFailed; - - /// - /// 重连失败之后触发的事件 - /// - public event EventHandler OnReconnectFailed - { - add - { - m_OnReconnectFailed += value; - } - remove - { - m_OnReconnectFailed -= value; - } - } - - /// - /// Invokes the state of the network. - /// - /// If set to true broken. - internal void InvokeNetworkState(bool available = false) - { - if (this.NetworkState.Available == available) return; - SetNetworkState(available); - PrintLog(string.Format("network connectivity is {0} now", available)); - // 如果断线产生的原因是客户端掉线而不是服务端踢下线,则应该开始自动重连 - var reasonShouldReconnect = new int[] { 0, 1006, 4107 }; - if (this.NetworkState.Available && reasonShouldReconnect.Contains(this.WebSocketState.ClosedCode)) - { - StartAutoReconnect(); - } - } - - internal void SetNetworkState(bool available = true) - { - this.NetworkState = new NetworkStateOptions() - { - Available = available - }; - } - - private EventHandler m_NoticeReceived; - public event EventHandler NoticeReceived - { - add - { - m_NoticeReceived += value; - } - remove - { - m_NoticeReceived -= value; - } - } - - private void WebSocketClient_OnMessage(string obj) - { - try - { - var estimatedData = Json.Parse(obj) as IDictionary; - var validator = AVIMNotice.IsValidLeanCloudProtocol(estimatedData); - if (validator) - { - var notice = new AVIMNotice(estimatedData); - m_NoticeReceived?.Invoke(this, notice); - } - } - catch (Exception ex) - { - if (ex.InnerException != null) - { - PrintLog(ex.InnerException.Source); - } - if (ex.Source != null) - { - PrintLog(ex.Source); - } - - PrintLog(ex.Message); - } - } - - /// - /// 设定监听者 - /// - /// - /// - public void SubscribeNoticeReceived(IAVIMListener listener, Func runtimeHook = null) - { - this.NoticeReceived += new EventHandler((sender, notice) => - { - var approved = runtimeHook == null ? listener.ProtocolHook(notice) : runtimeHook(notice) && listener.ProtocolHook(notice); - if (approved) - { - listener.OnNoticeReceived(notice); - } - }); - } - - /// - /// 初始化配置项 - /// - public struct Configuration - { - /// - /// 签名工厂 - /// - public ISignatureFactory SignatureFactory { get; set; } - /// - /// 自定义 WebSocket 客户端 - /// - public IWebSocketClient WebSocketClient { get; set; } - /// - /// LeanCloud App Id - /// - public string ApplicationId { get; set; } - /// - /// LeanCloud App Key - /// - public string ApplicationKey { get; set; } - - /// - /// 登录的时候告知服务器,本次登录所使用的离线消息策略 - /// - public OfflineMessageStrategy OfflineMessageStrategy { get; set; } - } - - /// - ///登录时的离线消息下发策略 - /// - public enum OfflineMessageStrategy - { - /// - /// 服务器将所有离线消息一次性在登录之后马上下发下来 - /// - Default = 1, - - /// - /// 不再下发未读消息,而是下发对话的未读通知,告知客户端有哪些对话处于未读状态 - /// - [Obsolete("该策略已被废弃,请使用 UnreadAck")] - UnreadNotice = 2, - - /// - /// ack 和 read 分离, ack 不会清理未读消息 - /// - UnreadAck = 3 - } - - /// - /// 当前配置 - /// - public Configuration CurrentConfiguration { get; internal set; } - /// - /// 初始化实时消息客户端 - /// - /// - public AVRealtime(Configuration config) - { - lock (mutex) - { - if ((int)config.OfflineMessageStrategy == 0) - { - config.OfflineMessageStrategy = OfflineMessageStrategy.UnreadAck; - } - - CurrentConfiguration = config; - if (CurrentConfiguration.WebSocketClient != null) - { - webSocketController = CurrentConfiguration.WebSocketClient; - } - if (CurrentConfiguration.SignatureFactory != null) - { - this.SignatureFactory = CurrentConfiguration.SignatureFactory; - } - ReconnectOptions = new AVIMReconnectOptions() - { - Interval = 5, - Retry = 120 - }; - - - RegisterMessageType(); - RegisterMessageType(); - RegisterMessageType(); - RegisterMessageType(); - RegisterMessageType(); - RegisterMessageType(); - RegisterMessageType(); - RegisterMessageType(); - RegisterMessageType(); - - // 注册服务端 goaway 指令 - var goAwayListener = new GoAwayListener(); - goAwayListener.OnGoAway += () => { - RouterController.ClearCache().ContinueWith(_ => { - reborn = true; - // 关闭 WebSocket - AVWebSocketClient.Disconnect(); - }); - }; - SubscribeNoticeReceived(goAwayListener); - - reconnectTimes = 0; - } - } - - /// - /// 初始化实时消息客户端 - /// - /// - /// - public AVRealtime(string applicationId, string applicationKey) - : this(new Configuration() - { - ApplicationId = applicationId, - ApplicationKey = applicationKey, - OfflineMessageStrategy = OfflineMessageStrategy.UnreadAck - }) - { - - } - - #region websocket log - internal static Action LogTracker { get; private set; } - /// - /// 打开 WebSocket 日志 - /// - /// - public static void WebSocketLog(Action trace) - { - LogTracker = trace; - } - public static void PrintLog(string log) - { - if (AVRealtime.LogTracker != null) - { - AVRealtime.LogTracker(log); - } - } - #endregion - - /// - /// 创建 Client - /// - /// - /// - /// 设备唯一的 Id。如果是 iOS 设备,需要将 iOS 推送使用的 DeviceToken 作为 deviceId 传入 - /// 是否强制加密 wss 链接 - /// - /// - public Task CreateClientAsync(string clientId, - string tag = null, - string deviceId = null, - bool secure = true, - CancellationToken cancellationToken = default(CancellationToken)) - { - lock (mutex) - { - var client = PreLogIn(clientId, tag, deviceId); - - AVRealtime.PrintLog("begin OpenAsync."); - return OpenAsync(secure, Subprotocol, true, cancellationToken).OnSuccess(t => - { - if (!t.Result) - { - return Task.FromResult(null); - } - AVRealtime.PrintLog("websocket server connected, begin to open sesstion."); - SetNetworkState(); - var cmd = new SessionCommand() - .UA(VersionString) - .Tag(tag) - .DeviceId(deviceId) - .Option("open") - .PeerId(clientId); - - ToggleNotification(true); - return AttachSignature(cmd, this.SignatureFactory.CreateConnectSignature(clientId)); - - }).Unwrap().OnSuccess(x => - { - var cmd = x.Result; - if (cmd == null) - { - return Task.FromResult>>(null); - } - return this.RunCommandAsync(cmd); - }).Unwrap().OnSuccess(s => - { - if (s.Result == null) - { - return null; - } - AVRealtime.PrintLog("sesstion opened."); - state = Status.Online; - ToggleHeartBeating(true); - var response = s.Result.Item2; - if (response.ContainsKey("st")) - { - _sesstionToken = response["st"] as string; - } - if (response.ContainsKey("stTtl")) - { - var stTtl = long.Parse(response["stTtl"].ToString()); - _sesstionTokenExpire = DateTime.Now.ToUnixTimeStamp() + stTtl * 1000; - } - AfterLogIn(client); - return client; - }); - } - } - - - - /// - /// Creates the client async. - /// - /// The client async. - /// User. - /// Tag. - /// Device identifier. - /// If set to true secure. - public Task CreateClientAsync(AVUser user = null, - string tag = null, - string deviceId = null, - bool secure = true, - CancellationToken cancellationToken = default(CancellationToken)) - { - AVIMClient client = null; - AVRealtime.PrintLog("begin OpenAsync."); - return OpenAsync(secure, Subprotocol, true, cancellationToken).OnSuccess(openTask => - { - AVRealtime.PrintLog("OpenAsync OnSuccess. begin send open sesstion cmd."); - var userTask = Task.FromResult(user); - if (user == null) - userTask = AVUser.GetCurrentUserAsync(); - - return userTask; - }).Unwrap().OnSuccess(u => - { - var theUser = u.Result; - return AVCloud.RequestRealtimeSignatureAsync(theUser); - }).Unwrap().OnSuccess(signTask => - { - var signResult = signTask.Result; - var clientId = signResult.ClientId; - var nonce = signResult.Nonce; - var singnature = signResult.Signature; - var ts = signResult.Timestamp; - - client = PreLogIn(clientId, tag, deviceId); - ToggleNotification(true); - return this.OpenSessionAsync(clientId, tag, deviceId, nonce, ts, singnature, secure); - }).Unwrap().OnSuccess(s => - { - ToggleHeartBeating(true); - AfterLogIn(client); - return client; - }); - } - - #region pre-login - internal AVIMClient PreLogIn(string clientId, - string tag = null, - string deviceId = null) - { - var client = new AVIMClient(clientId, tag, this); - if (this.OnOfflineMessageReceived != null) - { - client.OnOfflineMessageReceived += this.OnOfflineMessageReceived; - } - _clientId = clientId; - _tag = tag; - _deviceId = deviceId; - if (_tag != null) - { - if (deviceId == null) - throw new ArgumentNullException(deviceId, "当 tag 不为空时,必须传入当前设备不变的唯一 id(deviceId)"); - } - - if (string.IsNullOrEmpty(clientId)) throw new Exception("当前 ClientId 为空,无法登录服务器。"); - - return client; - } - - internal void AfterLogIn(AVIMClient client) - { - if (clients == null) clients = new Dictionary(); - client.OnSessionClosed += (sender, e) => { - string clientId = (sender as AVIMClient).ClientId; - clients.Remove(clientId); - if (clients.Count == 0) { - LogOut(); - } - }; - clients[client.ClientId] = client; - } - - #endregion - - #region after-login - - - #endregion - - /// - /// 创建 Client - /// - /// - /// - /// 设备唯一的 Id。如果是 iOS 设备,需要将 iOS 推送使用的 DeviceToken 作为 deviceId 传入 - /// 是否强制加密 wss 链接 - /// - /// - [Obsolete("CreateClient is deprecated, please use CreateClientAsync instead.")] - public Task CreateClient( - string clientId, - string tag = null, - string deviceId = null, - bool secure = true, - CancellationToken cancellationToken = default(CancellationToken)) - { - return this.CreateClientAsync(clientId, tag, deviceId, secure, cancellationToken); - } - - private bool _listening = false; - /// - /// websocket 事件的监听的开关 - /// - /// 是否打开 - public void ToggleNotification(bool toggle) - { - AVRealtime.PrintLog("ToggleNotification| toggle:" + toggle + "|listening: " + _listening); - if (toggle && !_listening) - { - AVWebSocketClient.OnClosed += WebsocketClient_OnClosed; - AVWebSocketClient.OnMessage += WebSocketClient_OnMessage; - _listening = true; - } - else if (!toggle && _listening) - { - AVWebSocketClient.OnClosed -= WebsocketClient_OnClosed; - AVWebSocketClient.OnMessage -= WebSocketClient_OnMessage; - _listening = false; - } - } - - //public void ToggleOfflineNotification(bool toggle) - //{ - // if (toggle) - // { - // PCLWebsocketClient.OnMessage += WebSocketClient_OnMessage_On_Session_Opening; - // } - // else - // { - // PCLWebsocketClient.OnMessage -= WebSocketClient_OnMessage_On_Session_Opening; - // } - //} - - //private void WebSocketClient_OnMessage_On_Session_Opening(string obj) - //{ - // AVRealtime.PrintLog("offline<=" + obj); - //} - - - string _beatPacket = "{}"; - bool _heartBeatingToggle = true; - IAVTimer _heartBeatingTimer; - /// - /// 主动发送心跳包 - /// - /// 是否开启 - /// 时间间隔 - /// 心跳包的内容,默认是个空的 {} - public void ToggleHeartBeating(bool toggle = true, double interval = 60000, string beatPacket = "{}") - { - this._heartBeatingToggle = toggle; - if (!string.Equals(_beatPacket, beatPacket)) _beatPacket = beatPacket; - - if (_heartBeatingTimer == null && this._heartBeatingToggle) - { - _heartBeatingTimer = new AVTimer(); - _heartBeatingTimer.Elapsed += SendHeartBeatingPacket; - _heartBeatingTimer.Interval = interval; - _heartBeatingTimer.Start(); - PrintLog("auto heart beating started."); - } - if (!this._heartBeatingToggle && _heartBeatingTimer != null) - { - _heartBeatingTimer.Stop(); - _heartBeatingTimer = null; - } - } - - void SendHeartBeatingPacket(object sender, TimerEventArgs e) - { - PrintLog("auto heart beating ticked by timer."); -#if MONO || UNITY - Dispatcher.Instance.Post(() => - { - KeepAlive(); - }); -#else - KeepAlive(); -#endif - } - - /// - /// Keeps the alive. - /// - public void KeepAlive() - { - try - { - var cmd = new AVIMCommand(); - RunCommandAsync(cmd).ContinueWith(t => - { - if (t.IsCanceled || t.IsFaulted || t.Exception != null) - { - InvokeNetworkState(); - } - }); - } - catch (Exception) - { - InvokeNetworkState(); - } - } - - internal bool sessionConflict = false; - internal bool loggedOut = false; - - internal bool CanReconnect - { - get - { - return !sessionConflict && !loggedOut && state == Status.Offline; - } - } - - /// - /// 开始自动重连 - /// - public void StartAutoReconnect() - { - - } - internal bool useSecondary = false; - internal bool reborn = false; - - internal Task LogInAsync(string clientId, - string tag = null, - string deviceId = null, - bool secure = true, - CancellationToken cancellationToken = default(CancellationToken)) - { - lock (mutex) - { - var cmd = new SessionCommand() - .UA(VersionString) - .Tag(tag) - .DeviceId(deviceId) - .Option("open") - .PeerId(clientId); - - var result = AttachSignature(cmd, this.SignatureFactory.CreateConnectSignature(clientId)).OnSuccess(_ => - { - return RunCommandAsync(cmd); - }).Unwrap().OnSuccess(t => - { - AVRealtime.PrintLog("sesstion opened."); - if (t.Exception != null) - { - var imException = t.Exception.InnerException as AVIMException; - throw imException; - } - state = Status.Online; - var response = t.Result.Item2; - if (response.ContainsKey("st")) - { - _sesstionToken = response["st"] as string; - } - if (response.ContainsKey("stTtl")) - { - var stTtl = long.Parse(response["stTtl"].ToString()); - _sesstionTokenExpire = DateTime.Now.ToUnixTimeStamp() + stTtl * 1000; - } - return t.Result; - }); - - return result; - } - - } - - internal Task OpenSessionAsync(string clientId, - string tag = null, - string deviceId = null, - string nonce = null, - long timestamp = 0, - string signature = null, - bool secure = true) - { - var cmd = new SessionCommand() - .UA(VersionString) - .Tag(tag) - .DeviceId(deviceId) - .Option("open") - .PeerId(clientId) - .Argument("n", nonce) - .Argument("t", timestamp) - .Argument("s", signature); - - return RunCommandAsync(cmd).OnSuccess(t => - { - AVRealtime.PrintLog("sesstion opened."); - if (t.Exception != null) - { - var imException = t.Exception.InnerException as AVIMException; - throw imException; - } - state = Status.Online; - var response = t.Result.Item2; - if (response.ContainsKey("st")) - { - _sesstionToken = response["st"] as string; - } - if (response.ContainsKey("stTtl")) - { - var stTtl = long.Parse(response["stTtl"].ToString()); - _sesstionTokenExpire = DateTime.Now.ToUnixTimeStamp() + stTtl * 1000; - } - return t.Result; - }); - - } - - /// - /// 自动重连 - /// - /// - Task AutoReconnect() - { - AVRealtime.PrintLog("AutoReconnect started."); - var reconnectingArgs = new AVIMReconnectingEventArgs() - { - ClientId = _clientId, - IsAuto = true, - SessionToken = _sesstionToken - }; - m_OnReconnecting?.Invoke(this, reconnectingArgs); - - var tcs = new TaskCompletionSource(); - Task task; - if (reborn) - { - AVRealtime.PrintLog("both preferred and secondary websockets are expired, so try to request RTM router to get a new pair"); - task = OpenAsync(this._secure, Subprotocol, true); - } else { - var websocketServer = _wss; - if (useSecondary) { - AVRealtime.PrintLog(string.Format("preferred websocket server ({0}) network broken, take secondary server({1}) :", _wss, _secondaryWss)); - websocketServer = _secondaryWss; - } - task = OpenAsync(websocketServer, Subprotocol, true); - } - - task.ContinueWith(t => - { - if (t.IsFaulted || t.IsCanceled) { - state = Status.Reconnecting; - var reconnectFailedArgs = new AVIMReconnectFailedArgs() { - ClientId = _clientId, - IsAuto = true, - SessionToken = _sesstionToken, - FailedCode = 0// network broken. - }; - m_OnReconnectFailed?.Invoke(this, reconnectFailedArgs); - state = Status.Offline; - tcs.SetException(t.Exception); - throw t.Exception; - } else { - state = Status.Opened; - SetNetworkState(); - - void onClose(int code, string reason, string detail) { - AVRealtime.PrintLog("disconnect when open session"); - var ex = new Exception("connection is closed"); - tcs.SetException(ex); - AVWebSocketClient.OnClosed -= onClose; - throw ex; - }; - AVWebSocketClient.OnClosed += onClose; - - if (this.IsSesstionTokenExpired) { - AVRealtime.PrintLog("session is expired, auto relogin with clientId :" + _clientId); - return this.LogInAsync(_clientId, this._tag, this._deviceId, this._secure).ContinueWith(o => { - AVWebSocketClient.OnClosed -= onClose; - return !o.IsFaulted; - }); - } else { - var sessionCMD = new SessionCommand().UA(VersionString).R(1); - - if (string.IsNullOrEmpty(_tag)) { - sessionCMD = sessionCMD.Tag(_tag).SessionToken(this._sesstionToken); - } - - var cmd = sessionCMD.Option("open") - .PeerId(_clientId); - - AVRealtime.PrintLog("reopen session with session token :" + _sesstionToken); - return RunCommandAsync(cmd).ContinueWith(o => { - AVWebSocketClient.OnClosed -= onClose; - return !o.IsFaulted; - }); - } - } - }).Unwrap().ContinueWith(s => - { - if (s.IsFaulted || s.Exception != null) - { - var reconnectFailedArgs = new AVIMReconnectFailedArgs() - { - ClientId = _clientId, - IsAuto = true, - SessionToken = _sesstionToken, - FailedCode = 1 - }; - m_OnReconnectFailed?.Invoke(this, reconnectFailedArgs); - state = Status.Offline; - tcs.SetException(s.Exception); - } - else - { - var reconnectedArgs = new AVIMReconnectedEventArgs() { - ClientId = _clientId, - IsAuto = true, - SessionToken = _sesstionToken, - }; - state = Status.Online; - m_OnReconnected?.Invoke(this, reconnectedArgs); - ToggleNotification(true); - ToggleHeartBeating(true); - tcs.SetResult(true); - } - }); - - return tcs.Task; - } - - - - #region register IAVIMMessage - /// - /// Registers the subtype of the message. - /// - /// The 1st type parameter. - public void RegisterMessageType() where T : IAVIMMessage - { - AVIMCorePlugins.Instance.FreeStyleClassingController.RegisterSubclass(typeof(T)); - } - #endregion - - /// - /// open websocket with default configurations. - /// - /// - public Task OpenAsync(bool secure = true) - { - return this.OpenAsync(secure, null); - } - - /// - /// Open websocket connection. - /// - /// The async. - /// If set to true secure. - /// Subprotocol. - /// If set to true enforce. - /// Cancellation token. - public Task OpenAsync(bool secure, string subprotocol = null, bool enforce = false, CancellationToken cancellationToken = default(CancellationToken)) - { - _secure = secure; - if (state == Status.Online && !enforce) - { - AVRealtime.PrintLog("state is Status.Online."); - return Task.FromResult(true); - } - - if (AVClient.CurrentConfiguration.RealtimeServer != null) - { - _wss = AVClient.CurrentConfiguration.RealtimeServer; - AVRealtime.PrintLog("use configuration realtime server with url: " + _wss); - return OpenAsync(_wss, subprotocol, enforce); - } - var routerUrl = AVClient.CurrentConfiguration.RTMServer; - return RouterController.GetAsync(routerUrl, secure, cancellationToken).OnSuccess(r => - { - var routerState = r.Result; - if (routerState == null) - { - return Task.FromResult(false); - } - _wss = routerState.server; - _secondaryWss = routerState.secondary; - state = Status.Connecting; - AVRealtime.PrintLog("push router give a url :" + _wss); - return OpenAsync(routerState.server, subprotocol, enforce); - }).Unwrap(); - } - - /// - /// open webcoket connection with cloud. - /// - /// wss address - /// subprotocol for websocket - /// - /// - public Task OpenAsync(string url, string subprotocol = null, bool enforce = false, CancellationToken cancellationToken = default(CancellationToken)) - { - if (AVWebSocketClient.IsOpen && !enforce) - { - AVRealtime.PrintLog(url + "is already connectd."); - return Task.FromResult(true); - } - - AVRealtime.PrintLog("websocket try to connect url :" + url + " with subprotocol: " + subprotocol); - AVRealtime.PrintLog(url + " \tconnecting..."); - - return AVWebSocketClient.Connect(url, subprotocol); - } - - /// - /// send websocket command to Realtime server. - /// - /// - /// - public Task>> RunCommandAsync(AVIMCommand command) - { - command.AppId(AVClient.CurrentConfiguration.ApplicationId); - return this.AVIMCommandRunner.RunCommandAsync(command); - } - - /// - /// - /// - /// - public void RunCommand(AVIMCommand command) - { - command.AppId(AVClient.CurrentConfiguration.ApplicationId); - this.AVIMCommandRunner.RunCommand(command); - } - - internal Task AttachSignature(AVIMCommand command, Task SignatureTask) - { - AVRealtime.PrintLog("begin to attach singature."); - var tcs = new TaskCompletionSource(); - if (SignatureTask == null) - { - tcs.SetResult(command); - return tcs.Task; - } - return SignatureTask.OnSuccess(_ => - { - if (_.Result != null) - { - var signature = _.Result; - command.Argument("t", signature.Timestamp); - command.Argument("n", signature.Nonce); - command.Argument("s", signature.SignatureContent); - AVRealtime.PrintLog("AttachSignature ended.t:" + signature.Timestamp + ";n:" + signature.Nonce + ";s:" + signature.SignatureContent); - } - return command; - }); - } - - #region log out and clean event subscribtion - private void WebsocketClient_OnClosed(int errorCode, string reason, string detail) - { - PrintLog(string.Format("websocket closed with code is {0},reason is {1} and detail is {2}", errorCode, reason, detail)); - state = Status.Offline; - - ToggleNotification(false); - ToggleHeartBeating(false); - - var disconnectEventArgs = new AVIMDisconnectEventArgs(errorCode, reason, detail); - m_OnDisconnected?.Invoke(this, disconnectEventArgs); - - this.WebSocketState = new WebSocketStateOptions() - { - ClosedCode = errorCode - }; - PrepareReconnect(); - } - - private void WebsocketClient_OnError(string obj) - { - PrintLog("error:" + obj); - // 如果遇到 WebSocket 错误之后,先关闭,再按断线处理 - AVWebSocketClient.Close(); - WebsocketClient_OnClosed(0, obj, string.Empty); - } - - void PrepareReconnect() { - AVRealtime.PrintLog("Prepare Reconnect"); - Task.Delay(RECONNECT_DELAY).ContinueWith(_ => { - // 开启重连 - AutoReconnect().ContinueWith(t => { - if (t.IsFaulted) { - // 重连失败,延迟再次重连 - reconnectTimes++; - AVRealtime.PrintLog(String.Format("reconnect {0} times", reconnectTimes)); - if (reconnectTimes >= RECONNECT_FROM_APP_ROUTER) { - // 如果大于当前服务地址的最大重连次数,则清空 Router 后重新重连 - RouterController.ClearCache().ContinueWith(__ => { - reborn = true; - PrepareReconnect(); - }); - - } else if (reconnectTimes >= RECONNECT_USE_SECONDARY_TIMES) { - // 如果大于单台 IM 服务器的重连次数,则启用备用服务器 - useSecondary = true; - PrepareReconnect(); - } else { - PrepareReconnect(); - } - } else { - // 重连成功 - reconnectTimes = 0; - reborn = false; - useSecondary = false; - } - }); - }); - } - - internal void LogOut() - { - State = Status.Closed; - loggedOut = true; - Dispose(); - AVWebSocketClient.Close(); - } - - internal void Dispose() - { - var toggle = false; - ToggleNotification(toggle); - ToggleHeartBeating(toggle); - - if (m_NoticeReceived != null) - { - foreach (Delegate d in m_NoticeReceived.GetInvocationList()) - { - m_NoticeReceived -= (EventHandler)d; - } - } - if (m_OnDisconnected != null) - { - foreach (Delegate d in m_OnDisconnected.GetInvocationList()) - { - m_OnDisconnected -= (EventHandler)d; - } - } - } - #endregion - - static AVRealtime() - { -#if MONO || UNITY - versionString = "net-unity/" + Version; -#else - versionString = "net-universal/" + Version; -#endif - } - - private static readonly string versionString; - internal static string VersionString - { - get - { - return versionString; - } - } - - internal static System.Version Version - { - get - { - AssemblyName assemblyName = new AssemblyName(typeof(AVRealtime).GetTypeInfo().Assembly.FullName); - return assemblyName.Version; - } - } - } -} diff --git a/RTM/RTM/Public/IAVIMListener.cs b/RTM/RTM/Public/IAVIMListener.cs deleted file mode 100644 index 366f4a2..0000000 --- a/RTM/RTM/Public/IAVIMListener.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud.Realtime -{ - /// - /// WebSocket 监听服务端事件通知的接口 - /// 所有基于协议层的事件监听都需要实现这个接口,然后自定义监听协议。 - /// - public interface IAVIMListener - { - /// - /// 监听的协议 Hook - /// 例如,消息的协议是 direct 命令,因此消息监听需要判断 == "direct" 才可以调用 - /// - /// - /// - bool ProtocolHook(AVIMNotice notice); - - ///// - ///// 如果 返回 true,则会启动 NoticeAction 里面的回调逻辑 - ///// - //Action NoticeAction { get; set; } - - /// - /// 如果 返回 true,则会启动 NoticeAction 里面的回调逻辑 - /// - void OnNoticeReceived(AVIMNotice notice); - } -} diff --git a/RTM/RTM/Public/IAVIMMessage.cs b/RTM/RTM/Public/IAVIMMessage.cs deleted file mode 100644 index f797485..0000000 --- a/RTM/RTM/Public/IAVIMMessage.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud.Realtime -{ - /// - /// 消息接口 - /// 所有消息必须实现这个接口 - /// - public interface IAVIMMessage - { - /// - /// Serialize this instance. - /// - /// The serialize. - string Serialize(); - - /// - /// Validate the specified msgStr. - /// - /// The validate. - /// Message string. - bool Validate(string msgStr); - - /// - /// Deserialize the specified msgStr. - /// - /// The deserialize. - /// Message string. - IAVIMMessage Deserialize(string msgStr); - - /// - /// Gets or sets the conversation identifier. - /// - /// The conversation identifier. - string ConversationId { get; set; } - - /// - /// Gets or sets from client identifier. - /// - /// From client identifier. - string FromClientId { get; set; } - - /// - /// Gets or sets the identifier. - /// - /// The identifier. - string Id { get; set; } - - /// - /// Gets or sets the server timestamp. - /// - /// The server timestamp. - long ServerTimestamp { get; set; } - - /// - /// Gets or sets the rcp timestamp. - /// - /// The rcp timestamp. - long RcpTimestamp { get; set; } - - long UpdatedAt { get; set; } - - - #region mention features. - /// - /// Gets or sets a value indicating whether this mention all. - /// - /// true if mention all; otherwise, false. - bool MentionAll { get; set; } - - /// - /// Gets or sets the mention list. - /// - /// The mention list. - IEnumerable MentionList { get; set; } - #endregion - - } -} diff --git a/RTM/RTM/Public/ICacheEngine.cs b/RTM/RTM/Public/ICacheEngine.cs deleted file mode 100644 index 1c20ec7..0000000 --- a/RTM/RTM/Public/ICacheEngine.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud.Realtime -{ - public interface ISQLStorage - { - - } -} diff --git a/RTM/RTM/Public/ISignatureFactory.cs b/RTM/RTM/Public/ISignatureFactory.cs deleted file mode 100644 index ffda11a..0000000 --- a/RTM/RTM/Public/ISignatureFactory.cs +++ /dev/null @@ -1,131 +0,0 @@ -using LeanCloud; -using LeanCloud.Storage.Internal; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - - -namespace LeanCloud.Realtime -{ - /// - /// 对话操作的签名类型,比如讲一个 client id 加入到对话中 - /// - /// - public enum ConversationSignatureAction - { - /// - /// add 加入对话和邀请对方加入对话 - /// - Add, - /// - /// remove 当前 client Id 离开对话和将其他人踢出对话 - /// - Remove - } - - /// - /// - /// - public interface ISignatureFactory - { - - /// - /// 构建登录签名 - /// - /// 需要登录到云端服务器的 client Id - /// - Task CreateConnectSignature(string clientId); - - /// - /// - /// - /// - /// - /// - Task CreateStartConversationSignature(string clientId, IEnumerable targetIds); - - /// - /// - /// - /// - /// - /// - /// 需要签名的操作 - /// - Task CreateConversationSignature(string conversationId, string clientId, IEnumerable targetIds, ConversationSignatureAction action); - } - - internal class DefaulSiganatureFactory : ISignatureFactory - { - Task ISignatureFactory.CreateConnectSignature(string clientId) - { - return Task.FromResult(null); - } - - Task ISignatureFactory.CreateConversationSignature(string conversationId, string clientId, IEnumerable targetIds, ConversationSignatureAction action) - { - return Task.FromResult(null); - } - - Task ISignatureFactory.CreateStartConversationSignature(string clientId, IEnumerable targetIds) - { - return Task.FromResult(null); - } - } - - public class LeanEngineSignatureFactory : ISignatureFactory - { - public Task CreateConnectSignature(string clientId) - { - var data = new Dictionary(); - data.Add("client_id", clientId); - return AVCloud.CallFunctionAsync>("connect", data).OnSuccess(_ => - { - var jsonData = _.Result; - var s = jsonData["signature"].ToString(); - var n = jsonData["nonce"].ToString(); - var t = long.Parse(jsonData["timestamp"].ToString()); - var signature = new AVIMSignature(s, t, n); - return signature; - }); - } - - public Task CreateStartConversationSignature(string clientId, IEnumerable targetIds) - { - var data = new Dictionary(); - data.Add("client_id", clientId); - data.Add("members", targetIds.ToList()); - return AVCloud.CallFunctionAsync>("startConversation", data).OnSuccess(_ => - { - var jsonData = _.Result; - var s = jsonData["signature"].ToString(); - var n = jsonData["nonce"].ToString(); - var t = long.Parse(jsonData["timestamp"].ToString()); - var signature = new AVIMSignature(s, t, n); - return signature; - }); - } - - public Task CreateConversationSignature(string conversationId, string clientId, IEnumerable targetIds, ConversationSignatureAction action) - { - var actionList = new string[] { "invite", "kick" }; - var data = new Dictionary(); - data.Add("client_id", clientId); - data.Add("conv_id", conversationId); - data.Add("members", targetIds.ToList()); - data.Add("action", actionList[(int)action]); - return AVCloud.CallFunctionAsync>("oprateConversation", data).OnSuccess(_ => - { - var jsonData = _.Result; - var s = jsonData["signature"].ToString(); - var n = jsonData["nonce"].ToString(); - var t = long.Parse(jsonData["timestamp"].ToString()); - var signature = new AVIMSignature(s, t, n); - return signature; - }); - } - - } -} diff --git a/RTM/RTM/Public/Listener/AVIMConversationListener.cs b/RTM/RTM/Public/Listener/AVIMConversationListener.cs deleted file mode 100644 index 77f931a..0000000 --- a/RTM/RTM/Public/Listener/AVIMConversationListener.cs +++ /dev/null @@ -1,256 +0,0 @@ -using LeanCloud.Storage.Internal; -using LeanCloud.Realtime.Internal; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud.Realtime -{ - /// - /// 对话中成员变动的事件参数,它提供被操作的对话(Conversation),操作类型(AffectedType) - /// 受影响的成员列表(AffectedMembers) - /// - public class AVIMOnMembersChangedEventArgs : EventArgs - { - /// - /// 本次成员变动中被操作的具体对话(AVIMConversation)的对象 - /// - public AVIMConversation Conversation { get; set; } - - /// - /// 变动的类型 - /// - public AVIMConversationEventType AffectedType { get; internal set; } - - /// - /// 受影响的成员的 Client Ids - /// - public IList AffectedMembers { get; set; } - - /// - /// 操作人的 Client ClientId - /// - public string Oprator { get; set; } - - /// - /// 操作的时间,已转化为本地时间 - /// - public DateTime OpratedTime { get; set; } - } - - /// - /// 变动的类型,目前支持如下: - /// 1、Joined:当前 Client 主动加入,案例:当 A 主动加入到对话,A 将收到 Joined 事件响应,其余的成员收到 MembersJoined 事件响应 - /// 2、Left:当前 Client 主动退出,案例:当 A 从对话中退出,A 将收到 Left 事件响应,其余的成员收到 MembersLeft 事件响应 - /// 3、MembersJoined:某个成员加入(区别于Joined和Kicked),案例:当 A 把 B 加入到对话中,C 将收到 MembersJoined 事件响应 - /// 4、MembersLeft:某个成员加入(区别于Joined和Kicked),案例:当 A 把 B 从对话中剔除,C 将收到 MembersLeft 事件响应 - /// 5、Invited:当前 Client 被邀请加入,案例:当 A 被 B 邀请加入到对话中,A 将收到 Invited 事件响应,B 将收到 Joined ,其余的成员收到 MembersJoined 事件响应 - /// 6、Kicked:当前 Client 被剔除,案例:当 A 被 B 从对话中剔除,A 将收到 Kicked 事件响应,B 将收到 Left,其余的成员收到 MembersLeft 事件响应 - /// - public enum AVIMConversationEventType - { - /// - /// 自身主动加入 - /// - Joined = 1, - /// - /// 自身主动离开 - /// - Left, - /// - /// 他人加入 - /// - MembersJoined, - /// - /// 他人离开 - /// - MembersLeft, - /// - /// 自身被邀请加入 - /// - Invited, - /// - /// 自身被他人剔除 - /// - Kicked - } - - #region AVIMMembersJoinListener - //when Members joined or invited by member,this listener will invoke AVIMOnMembersJoinedEventArgs event. - /// - /// 对话中有成员加入的时候,在改对话中的其他成员都会触发 事件 - /// - public class AVIMMembersJoinListener : IAVIMListener - { - - private EventHandler m_OnMembersJoined; - /// - /// 有成员加入到对话时,触发的事件 - /// - public event EventHandler OnMembersJoined - { - add - { - m_OnMembersJoined += value; - } - remove - { - m_OnMembersJoined -= value; - } - } - - public virtual void OnNoticeReceived(AVIMNotice notice) - { - if (m_OnMembersJoined != null) - { - var joinedMembers = AVDecoder.Instance.DecodeList(notice.RawData["m"]); - var ivitedBy = notice.RawData["initBy"].ToString(); - var conersationId = notice.RawData["cid"].ToString(); - var args = new AVIMOnMembersJoinedEventArgs() - { - ConversationId = conersationId, - InvitedBy = ivitedBy, - JoinedMembers = joinedMembers - }; - m_OnMembersJoined.Invoke(this, args); - } - } - - public virtual bool ProtocolHook(AVIMNotice notice) - { - if (notice.CommandName != "conv") return false; - if (!notice.RawData.ContainsKey("op")) return false; - var op = notice.RawData["op"].ToString(); - if (!op.Equals("members-joined")) return false; - return true; - } - } - #endregion - - #region AVIMMembersLeftListener - // when Members left or kicked by member,this listener will invoke AVIMOnMembersJoinedEventArgs event. - /// - /// 对话中有成员加入的时候,在改对话中的其他成员都会触发 OnMembersJoined 事件 - /// - public class AVIMMembersLeftListener : IAVIMListener - { - private EventHandler m_OnMembersLeft; - /// - /// 有成员加入到对话时,触发的事件 - /// - public event EventHandler OnMembersLeft - { - add - { - m_OnMembersLeft += value; - } - remove - { - m_OnMembersLeft -= value; - } - } - public virtual void OnNoticeReceived(AVIMNotice notice) - { - if (m_OnMembersLeft != null) - { - var leftMembers = AVDecoder.Instance.DecodeList(notice.RawData["m"]); - var kickedBy = notice.RawData["initBy"].ToString(); - var conersationId = notice.RawData["cid"].ToString(); - var args = new AVIMOnMembersLeftEventArgs() - { - ConversationId = conersationId, - KickedBy = kickedBy, - LeftMembers = leftMembers - }; - m_OnMembersLeft.Invoke(this, args); - } - } - - public virtual bool ProtocolHook(AVIMNotice notice) - { - if (notice.CommandName != "conv") return false; - if (!notice.RawData.ContainsKey("op")) return false; - var op = notice.RawData["op"].ToString(); - if (!op.Equals("members-left")) return false; - return true; - } - } - #endregion - - #region AVIMInvitedListener - public class AVIMInvitedListener : IAVIMListener - { - private EventHandler m_OnInvited; - public event EventHandler OnInvited { - add { - m_OnInvited += value; - } remove { - m_OnInvited -= value; - } - } - public void OnNoticeReceived(AVIMNotice notice) - { - if (m_OnInvited != null) - { - var ivitedBy = notice.RawData["initBy"].ToString(); - var conersationId = notice.RawData["cid"].ToString(); - var args = new AVIMOnInvitedEventArgs() - { - ConversationId = conersationId, - InvitedBy = ivitedBy, - }; - m_OnInvited.Invoke(this, args); - } - } - - public bool ProtocolHook(AVIMNotice notice) - { - if (notice.CommandName != "conv") return false; - if (!notice.RawData.ContainsKey("op")) return false; - var op = notice.RawData["op"].ToString(); - if (!op.Equals("joined")) return false; - return true; - } - } - #endregion - - #region AVIMKickedListener - public class AVIMKickedListener : IAVIMListener - { - private EventHandler m_OnKicked; - public event EventHandler OnKicked { - add { - m_OnKicked += value; - } remove { - m_OnKicked -= value; - } - } - public void OnNoticeReceived(AVIMNotice notice) - { - if (m_OnKicked != null) - { - var kickcdBy = notice.RawData["initBy"].ToString(); - var conersationId = notice.RawData["cid"].ToString(); - var args = new AVIMOnKickedEventArgs() - { - ConversationId = conersationId, - KickedBy = kickcdBy, - }; - m_OnKicked.Invoke(this, args); - } - } - - public bool ProtocolHook(AVIMNotice notice) - { - if (notice.CommandName != "conv") return false; - if (!notice.RawData.ContainsKey("op")) return false; - var op = notice.RawData["op"].ToString(); - if (!op.Equals("left")) return false; - return true; - } - } - #endregion - -} diff --git a/RTM/RTM/Public/Listener/ConversationUnreadListener.cs b/RTM/RTM/Public/Listener/ConversationUnreadListener.cs deleted file mode 100644 index bd48b89..0000000 --- a/RTM/RTM/Public/Listener/ConversationUnreadListener.cs +++ /dev/null @@ -1,145 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using LeanCloud.Realtime.Internal; - -namespace LeanCloud.Realtime -{ - internal class ConversationUnreadListener : IAVIMListener - { - internal class UnreadConversationNotice : IEqualityComparer - { - internal readonly object mutex = new object(); - internal IAVIMMessage LastUnreadMessage { get; set; } - internal string ConvId { get; set; } - internal int UnreadCount { get; set; } - - public bool Equals(UnreadConversationNotice x, UnreadConversationNotice y) - { - return x.ConvId == y.ConvId; - } - - public int GetHashCode(UnreadConversationNotice obj) - { - return obj.ConvId.GetHashCode(); - } - - internal void AutomicIncrement() - { - lock (mutex) - { - UnreadCount++; - } - } - } - internal static readonly object sMutex = new object(); - internal static long NotifTime; - internal static HashSet UnreadConversations; - static ConversationUnreadListener() - { - UnreadConversations = new HashSet(new UnreadConversationNotice()); - NotifTime = DateTime.Now.ToUnixTimeStamp(); - } - - internal static void UpdateNotice(IAVIMMessage message) - { - lock (sMutex) - { - var convValidators = UnreadConversations.Where(c => c.ConvId == message.ConversationId); - if (convValidators != null) - { - if (convValidators.Count() > 0) - { - var currentNotice = convValidators.FirstOrDefault(); - currentNotice.AutomicIncrement(); - currentNotice.LastUnreadMessage = message; - } - else - { - var currentThread = new UnreadConversationNotice(); - currentThread.ConvId = message.ConversationId; - currentThread.LastUnreadMessage = message; - currentThread.AutomicIncrement(); - UnreadConversations.Add(currentThread); - } - } - } - } - internal static void ClearUnread(string convId) - { - UnreadConversations.Remove(Get(convId)); - } - internal static IEnumerable FindAllConvIds() - { - lock (sMutex) - { - return ConversationUnreadListener.UnreadConversations.Select(c => c.ConvId); - } - } - - internal static UnreadConversationNotice Get(string convId) - { - lock (sMutex) - { - var unreadValidator = ConversationUnreadListener.UnreadConversations.Where(c => c.ConvId == convId); - if (unreadValidator != null) - { - if (unreadValidator.Count() > 0) - { - var notice = unreadValidator.FirstOrDefault(); - return notice; - } - } - return null; - } - } - - public void OnNoticeReceived(AVIMNotice notice) - { - lock (sMutex) - { - if (notice.RawData.ContainsKey("convs")) - { - var unreadRawData = notice.RawData["convs"] as List; - if (notice.RawData.ContainsKey("notifTime")) - { - long.TryParse(notice.RawData["notifTime"].ToString(), out NotifTime); - } - foreach (var data in unreadRawData) - { - var dataMap = data as IDictionary; - if (dataMap != null) - { - var convId = dataMap["cid"].ToString(); - var ucn = Get(convId); - if (ucn == null) ucn = new UnreadConversationNotice(); - - ucn.ConvId = convId; - var unreadCount = 0; - Int32.TryParse(dataMap["unread"].ToString(), out unreadCount); - ucn.UnreadCount = unreadCount; - - #region restore last message for the conversation - if (dataMap.ContainsKey("data")) - { - var msgStr = dataMap["data"].ToString(); - var messageObj = AVRealtime.FreeStyleMessageClassingController.Instantiate(msgStr, dataMap); - ucn.LastUnreadMessage = messageObj; - } - - UnreadConversations.Add(ucn); - #endregion - } - } - } - } - } - - public bool ProtocolHook(AVIMNotice notice) - { - return notice.CommandName == "unread"; - } - } -} diff --git a/RTM/RTM/Public/Listener/GoAwayListener.cs b/RTM/RTM/Public/Listener/GoAwayListener.cs deleted file mode 100644 index 57ecb62..0000000 --- a/RTM/RTM/Public/Listener/GoAwayListener.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; - -namespace LeanCloud.Realtime { - /// - /// 强制被踢下线处理 - /// - internal class GoAwayListener : IAVIMListener { - Action onGoAway; - - public event Action OnGoAway { - add { - onGoAway += value; - } - remove { - onGoAway -= value; - } - } - - public void OnNoticeReceived(AVIMNotice notice) { - // TODO 退出并清理路由缓存 - onGoAway?.Invoke(); - } - - public bool ProtocolHook(AVIMNotice notice) { - return notice.CommandName == "goaway"; - } - } -} diff --git a/RTM/RTM/Public/Listener/MessagePatchListener.cs b/RTM/RTM/Public/Listener/MessagePatchListener.cs deleted file mode 100644 index b588190..0000000 --- a/RTM/RTM/Public/Listener/MessagePatchListener.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud.Realtime -{ - internal delegate void OnMessagePatch(IEnumerable messages); - internal class MessagePatchListener : IAVIMListener - { - public OnMessagePatch OnReceived { get; set; } - - public void OnNoticeReceived(AVIMNotice notice) - { - ICollection patchedMessages = new List(); - var msgObjs = notice.RawData["patches"] as IList; - if (msgObjs != null) - { - foreach (var msgObj in msgObjs) - { - var msgData = msgObj as IDictionary; - if (msgData != null) - { - var msgStr = msgData["data"] as string; - var message = AVRealtime.FreeStyleMessageClassingController.Instantiate(msgStr, msgData); - patchedMessages.Add(message); - } - } - } - if (OnReceived != null) - { - if (patchedMessages.Count > 0) - { - this.OnReceived(patchedMessages); - } - } - } - - public bool ProtocolHook(AVIMNotice notice) - { - if (notice.CommandName != "patch") return false; - if (!notice.RawData.ContainsKey("op")) return false; - if (notice.RawData["op"].ToString() != "modify") return false; - return true; - } - } -} diff --git a/RTM/RTM/Public/Listener/OfflineMessageListener.cs b/RTM/RTM/Public/Listener/OfflineMessageListener.cs deleted file mode 100644 index 983ad8a..0000000 --- a/RTM/RTM/Public/Listener/OfflineMessageListener.cs +++ /dev/null @@ -1,42 +0,0 @@ -using LeanCloud.Storage.Internal; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace LeanCloud.Realtime -{ - internal class OfflineMessageListener : IAVIMListener - { - private EventHandler m_OnOfflineMessageReceived; - public event EventHandler OnOfflineMessageReceived - { - add - { - m_OnOfflineMessageReceived += value; - } - remove - { - m_OnOfflineMessageReceived -= value; - } - } - public void OnNoticeReceived(AVIMNotice notice) - { - if (m_OnOfflineMessageReceived != null) - { - var msgStr = notice.RawData["msg"].ToString(); - var iMessage = AVRealtime.FreeStyleMessageClassingController.Instantiate(msgStr, notice.RawData); - var args = new AVIMMessageEventArgs(iMessage); - m_OnOfflineMessageReceived.Invoke(this, args); - } - - } - - public bool ProtocolHook(AVIMNotice notice) - { - if (notice.CommandName != "direct") return false; - if (!notice.RawData.ContainsKey("offline")) return false; - return true; - } - } -} diff --git a/RTM/RTM/Public/Listener/SessionListener.cs b/RTM/RTM/Public/Listener/SessionListener.cs deleted file mode 100644 index aad4c54..0000000 --- a/RTM/RTM/Public/Listener/SessionListener.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace LeanCloud.Realtime -{ - internal class SessionListener : IAVIMListener - { - private Action _onSessionClosed; - public event Action OnSessionClosed - { - add - { - _onSessionClosed += value; - } - remove - { - _onSessionClosed -= value; - } - } - public void OnNoticeReceived(AVIMNotice notice) - { - var code = 0; - if (notice.RawData.ContainsKey("code")) - { - int.TryParse(notice.RawData["code"].ToString(), out code); - } - - var reason = ""; - if (notice.RawData.ContainsKey("reason")) - { - reason = notice.RawData["reason"].ToString(); - } - - var detail = ""; - if (notice.RawData.ContainsKey("detail")) - { - detail = notice.RawData["detail"].ToString(); - } - - if (_onSessionClosed != null) - { - _onSessionClosed(code, reason, detail); - } - } - - public bool ProtocolHook(AVIMNotice notice) - { - if (notice.CommandName != "session") return false; - if (!notice.RawData.ContainsKey("op")) return false; - if (notice.RawData.ContainsKey("i")) return false; - if (notice.RawData["op"].ToString() != "closed") return false; - - return true; - } - } -} diff --git a/RTM/RTM/RTM.csproj b/RTM/RTM/RTM.csproj deleted file mode 100644 index 5f49628..0000000 --- a/RTM/RTM/RTM.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - - netstandard2.0 - 0.1.0 - - - - - - - - - - - - diff --git a/Storage/Storage/Internal/Codec/LCDecoder.cs b/Storage/Internal/Codec/LCDecoder.cs similarity index 100% rename from Storage/Storage/Internal/Codec/LCDecoder.cs rename to Storage/Internal/Codec/LCDecoder.cs diff --git a/Storage/Storage/Internal/Codec/LCEncoder.cs b/Storage/Internal/Codec/LCEncoder.cs similarity index 100% rename from Storage/Storage/Internal/Codec/LCEncoder.cs rename to Storage/Internal/Codec/LCEncoder.cs diff --git a/Storage/Storage/Internal/File/LCAWSUploader.cs b/Storage/Internal/File/LCAWSUploader.cs similarity index 100% rename from Storage/Storage/Internal/File/LCAWSUploader.cs rename to Storage/Internal/File/LCAWSUploader.cs diff --git a/Storage/Storage/Internal/File/LCMimeTypeMap.cs b/Storage/Internal/File/LCMimeTypeMap.cs similarity index 100% rename from Storage/Storage/Internal/File/LCMimeTypeMap.cs rename to Storage/Internal/File/LCMimeTypeMap.cs diff --git a/Storage/Storage/Internal/File/LCProgressableStreamContent.cs b/Storage/Internal/File/LCProgressableStreamContent.cs similarity index 100% rename from Storage/Storage/Internal/File/LCProgressableStreamContent.cs rename to Storage/Internal/File/LCProgressableStreamContent.cs diff --git a/Storage/Storage/Internal/File/LCQiniuUploader.cs b/Storage/Internal/File/LCQiniuUploader.cs similarity index 100% rename from Storage/Storage/Internal/File/LCQiniuUploader.cs rename to Storage/Internal/File/LCQiniuUploader.cs diff --git a/Storage/Storage/Internal/Http/LCHttpClient.cs b/Storage/Internal/Http/LCHttpClient.cs similarity index 100% rename from Storage/Storage/Internal/Http/LCHttpClient.cs rename to Storage/Internal/Http/LCHttpClient.cs diff --git a/Storage/Storage/Internal/Http/LeanCloudJsonConverter.cs b/Storage/Internal/Http/LeanCloudJsonConverter.cs similarity index 100% rename from Storage/Storage/Internal/Http/LeanCloudJsonConverter.cs rename to Storage/Internal/Http/LeanCloudJsonConverter.cs diff --git a/Storage/Storage/Internal/Object/LCBatch.cs b/Storage/Internal/Object/LCBatch.cs similarity index 100% rename from Storage/Storage/Internal/Object/LCBatch.cs rename to Storage/Internal/Object/LCBatch.cs diff --git a/Storage/Storage/Internal/Object/LCObjectData.cs b/Storage/Internal/Object/LCObjectData.cs similarity index 100% rename from Storage/Storage/Internal/Object/LCObjectData.cs rename to Storage/Internal/Object/LCObjectData.cs diff --git a/Storage/Storage/Internal/Object/LCSubClassInfo.cs b/Storage/Internal/Object/LCSubClassInfo.cs similarity index 100% rename from Storage/Storage/Internal/Object/LCSubClassInfo.cs rename to Storage/Internal/Object/LCSubClassInfo.cs diff --git a/Storage/Storage/Internal/Operation/ILCOperation.cs b/Storage/Internal/Operation/ILCOperation.cs similarity index 100% rename from Storage/Storage/Internal/Operation/ILCOperation.cs rename to Storage/Internal/Operation/ILCOperation.cs diff --git a/Storage/Storage/Internal/Operation/LCAddOperation.cs b/Storage/Internal/Operation/LCAddOperation.cs similarity index 100% rename from Storage/Storage/Internal/Operation/LCAddOperation.cs rename to Storage/Internal/Operation/LCAddOperation.cs diff --git a/Storage/Storage/Internal/Operation/LCAddRelationOperation.cs b/Storage/Internal/Operation/LCAddRelationOperation.cs similarity index 100% rename from Storage/Storage/Internal/Operation/LCAddRelationOperation.cs rename to Storage/Internal/Operation/LCAddRelationOperation.cs diff --git a/Storage/Storage/Internal/Operation/LCAddUniqueOperation.cs b/Storage/Internal/Operation/LCAddUniqueOperation.cs similarity index 100% rename from Storage/Storage/Internal/Operation/LCAddUniqueOperation.cs rename to Storage/Internal/Operation/LCAddUniqueOperation.cs diff --git a/Storage/Storage/Internal/Operation/LCDeleteOperation.cs b/Storage/Internal/Operation/LCDeleteOperation.cs similarity index 100% rename from Storage/Storage/Internal/Operation/LCDeleteOperation.cs rename to Storage/Internal/Operation/LCDeleteOperation.cs diff --git a/Storage/Storage/Internal/Operation/LCNumberOperation.cs b/Storage/Internal/Operation/LCNumberOperation.cs similarity index 100% rename from Storage/Storage/Internal/Operation/LCNumberOperation.cs rename to Storage/Internal/Operation/LCNumberOperation.cs diff --git a/Storage/Storage/Internal/Operation/LCRemoveOperation.cs b/Storage/Internal/Operation/LCRemoveOperation.cs similarity index 100% rename from Storage/Storage/Internal/Operation/LCRemoveOperation.cs rename to Storage/Internal/Operation/LCRemoveOperation.cs diff --git a/Storage/Storage/Internal/Operation/LCRemoveRelationOperation.cs b/Storage/Internal/Operation/LCRemoveRelationOperation.cs similarity index 100% rename from Storage/Storage/Internal/Operation/LCRemoveRelationOperation.cs rename to Storage/Internal/Operation/LCRemoveRelationOperation.cs diff --git a/Storage/Storage/Internal/Operation/LCSetOperation.cs b/Storage/Internal/Operation/LCSetOperation.cs similarity index 100% rename from Storage/Storage/Internal/Operation/LCSetOperation.cs rename to Storage/Internal/Operation/LCSetOperation.cs diff --git a/Storage/Storage/Internal/Query/ILCQueryCondition.cs b/Storage/Internal/Query/ILCQueryCondition.cs similarity index 100% rename from Storage/Storage/Internal/Query/ILCQueryCondition.cs rename to Storage/Internal/Query/ILCQueryCondition.cs diff --git a/Storage/Storage/Internal/Query/LCCompositionalCondition.cs b/Storage/Internal/Query/LCCompositionalCondition.cs similarity index 100% rename from Storage/Storage/Internal/Query/LCCompositionalCondition.cs rename to Storage/Internal/Query/LCCompositionalCondition.cs diff --git a/Storage/Storage/Internal/Query/LCEqualCondition.cs b/Storage/Internal/Query/LCEqualCondition.cs similarity index 100% rename from Storage/Storage/Internal/Query/LCEqualCondition.cs rename to Storage/Internal/Query/LCEqualCondition.cs diff --git a/Storage/Storage/Internal/Query/LCOperationCondition.cs b/Storage/Internal/Query/LCOperationCondition.cs similarity index 100% rename from Storage/Storage/Internal/Query/LCOperationCondition.cs rename to Storage/Internal/Query/LCOperationCondition.cs diff --git a/Storage/Storage/Internal/Query/LCRelatedCondition.cs b/Storage/Internal/Query/LCRelatedCondition.cs similarity index 100% rename from Storage/Storage/Internal/Query/LCRelatedCondition.cs rename to Storage/Internal/Query/LCRelatedCondition.cs diff --git a/Storage/Storage/LCACL.cs b/Storage/LCACL.cs similarity index 100% rename from Storage/Storage/LCACL.cs rename to Storage/LCACL.cs diff --git a/Storage/Storage/LCCloud.cs b/Storage/LCCloud.cs similarity index 100% rename from Storage/Storage/LCCloud.cs rename to Storage/LCCloud.cs diff --git a/Storage/Storage/LCException.cs b/Storage/LCException.cs similarity index 89% rename from Storage/Storage/LCException.cs rename to Storage/LCException.cs index e58fb46..acf2249 100644 --- a/Storage/Storage/LCException.cs +++ b/Storage/LCException.cs @@ -6,7 +6,7 @@ namespace LeanCloud.Storage { get; set; } - public string Message { + public new string Message { get; set; } diff --git a/Storage/Storage/LCFile.cs b/Storage/LCFile.cs similarity index 100% rename from Storage/Storage/LCFile.cs rename to Storage/LCFile.cs diff --git a/Storage/Storage/LCGeoPoint.cs b/Storage/LCGeoPoint.cs similarity index 100% rename from Storage/Storage/LCGeoPoint.cs rename to Storage/LCGeoPoint.cs diff --git a/Storage/Storage/LCObject.cs b/Storage/LCObject.cs similarity index 100% rename from Storage/Storage/LCObject.cs rename to Storage/LCObject.cs diff --git a/Storage/Storage/LCQuery.cs b/Storage/LCQuery.cs similarity index 100% rename from Storage/Storage/LCQuery.cs rename to Storage/LCQuery.cs diff --git a/Storage/Storage/LCRelation.cs b/Storage/LCRelation.cs similarity index 100% rename from Storage/Storage/LCRelation.cs rename to Storage/LCRelation.cs diff --git a/Storage/Storage/LCRole.cs b/Storage/LCRole.cs similarity index 100% rename from Storage/Storage/LCRole.cs rename to Storage/LCRole.cs diff --git a/Storage/Storage/LCUser.cs b/Storage/LCUser.cs similarity index 100% rename from Storage/Storage/LCUser.cs rename to Storage/LCUser.cs diff --git a/Storage/Storage/LCUserAuthDataLoginOption.cs b/Storage/LCUserAuthDataLoginOption.cs similarity index 100% rename from Storage/Storage/LCUserAuthDataLoginOption.cs rename to Storage/LCUserAuthDataLoginOption.cs diff --git a/Storage/Storage/LeanCloud.cs b/Storage/LeanCloud.cs similarity index 100% rename from Storage/Storage/LeanCloud.cs rename to Storage/LeanCloud.cs diff --git a/Storage/Source/Internal/AVCorePlugins.cs b/Storage/Source/Internal/AVCorePlugins.cs deleted file mode 100644 index 3b2fd06..0000000 --- a/Storage/Source/Internal/AVCorePlugins.cs +++ /dev/null @@ -1,384 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using LeanCloud.Storage.Internal; - -namespace LeanCloud.Storage.Internal -{ - public class AVPlugins : IAVCorePlugins - { - private static readonly object instanceMutex = new object(); - private static IAVCorePlugins instance; - public static IAVCorePlugins Instance - { - get - { - lock (instanceMutex) - { - instance = instance ?? new AVPlugins(); - return instance; - } - } - set - { - lock (instanceMutex) - { - instance = value; - } - } - } - - private readonly object mutex = new object(); - - #region Server Controllers - - private IHttpClient httpClient; - private IAppRouterController appRouterController; - private IAVCommandRunner commandRunner; - private IStorageController storageController; - - private IAVCloudCodeController cloudCodeController; - private IAVConfigController configController; - private IAVFileController fileController; - private IAVObjectController objectController; - private IAVQueryController queryController; - private IAVSessionController sessionController; - private IAVUserController userController; - private IObjectSubclassingController subclassingController; - - #endregion - - #region Current Instance Controller - - private IAVCurrentUserController currentUserController; - private IInstallationIdController installationIdController; - - #endregion - - public void Reset() - { - lock (mutex) - { - HttpClient = null; - AppRouterController = null; - CommandRunner = null; - StorageController = null; - - CloudCodeController = null; - FileController = null; - ObjectController = null; - SessionController = null; - UserController = null; - SubclassingController = null; - - CurrentUserController = null; - InstallationIdController = null; - } - } - - public IHttpClient HttpClient - { - get - { - lock (mutex) - { - httpClient = httpClient ?? new HttpClient(); - return httpClient; - } - } - set - { - lock (mutex) - { - httpClient = value; - } - } - } - - public IAppRouterController AppRouterController - { - get - { - lock (mutex) - { - appRouterController = appRouterController ?? new AppRouterController(); - return appRouterController; - } - } - set - { - lock (mutex) - { - appRouterController = value; - } - } - } - - public IAVCommandRunner CommandRunner - { - get - { - lock (mutex) - { - commandRunner = commandRunner ?? new AVCommandRunner(HttpClient, InstallationIdController); - return commandRunner; - } - } - set - { - lock (mutex) - { - commandRunner = value; - } - } - } - -#if !UNITY - public IStorageController StorageController - { - get - { - lock (mutex) - { - storageController = storageController ?? new StorageController(AVClient.CurrentConfiguration.ApplicationId); - return storageController; - } - } - set - { - lock (mutex) - { - storageController = value; - } - } - } -#endif -#if UNITY - public IStorageController StorageController - { - get - { - lock (mutex) - { - storageController = storageController ?? new StorageController(AVInitializeBehaviour.IsWebPlayer, AVClient.CurrentConfiguration.ApplicationId); - return storageController; - } - } - set - { - lock (mutex) - { - storageController = value; - } - } - } -#endif - - public IAVCloudCodeController CloudCodeController - { - get - { - lock (mutex) - { - cloudCodeController = cloudCodeController ?? new AVCloudCodeController(CommandRunner); - return cloudCodeController; - } - } - set - { - lock (mutex) - { - cloudCodeController = value; - } - } - } - - public IAVFileController FileController - { - get - { - lock (mutex) - { - if (AVClient.CurrentConfiguration.RegionValue == 0) - fileController = fileController ?? new QiniuFileController(CommandRunner); - else if (AVClient.CurrentConfiguration.RegionValue == 2) - fileController = fileController ?? new QCloudCosFileController(CommandRunner); - else if (AVClient.CurrentConfiguration.RegionValue == 1) - fileController = fileController ?? new AWSS3FileController(CommandRunner); - - return fileController; - } - } - set - { - lock (mutex) - { - fileController = value; - } - } - } - - public IAVConfigController ConfigController - { - get - { - lock (mutex) - { - if (configController == null) - { - configController = new AVConfigController(CommandRunner, StorageController); - } - return configController; - } - } - set - { - lock (mutex) - { - configController = value; - } - } - } - - public IAVObjectController ObjectController - { - get - { - lock (mutex) - { - objectController = objectController ?? new AVObjectController(CommandRunner); - return objectController; - } - } - set - { - lock (mutex) - { - objectController = value; - } - } - } - - public IAVQueryController QueryController - { - get - { - lock (mutex) - { - if (queryController == null) - { - queryController = new AVQueryController(CommandRunner); - } - return queryController; - } - } - set - { - lock (mutex) - { - queryController = value; - } - } - } - - public IAVSessionController SessionController - { - get - { - lock (mutex) - { - sessionController = sessionController ?? new AVSessionController(CommandRunner); - return sessionController; - } - } - set - { - lock (mutex) - { - sessionController = value; - } - } - } - - public IAVUserController UserController - { - get - { - lock (mutex) - { - userController = userController ?? new AVUserController(CommandRunner); - return userController; - } - } - set - { - lock (mutex) - { - userController = value; - } - } - } - - public IAVCurrentUserController CurrentUserController - { - get - { - lock (mutex) - { - currentUserController = currentUserController ?? new AVCurrentUserController(StorageController); - return currentUserController; - } - } - set - { - lock (mutex) - { - currentUserController = value; - } - } - } - - public IObjectSubclassingController SubclassingController - { - get - { - lock (mutex) - { - if (subclassingController == null) - { - subclassingController = new ObjectSubclassingController(); - subclassingController.AddRegisterHook(typeof(AVUser), () => CurrentUserController.ClearFromMemory()); - } - return subclassingController; - } - } - set - { - lock (mutex) - { - subclassingController = value; - } - } - } - - public IInstallationIdController InstallationIdController - { - get - { - lock (mutex) - { - installationIdController = installationIdController ?? new InstallationIdController(StorageController); - return installationIdController; - } - } - set - { - lock (mutex) - { - installationIdController = value; - } - } - } - } -} diff --git a/Storage/Source/Internal/AppRouter/AppRouterController.cs b/Storage/Source/Internal/AppRouter/AppRouterController.cs deleted file mode 100644 index 1e0c73d..0000000 --- a/Storage/Source/Internal/AppRouter/AppRouterController.cs +++ /dev/null @@ -1,103 +0,0 @@ -using System; -using System.Net; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using LeanCloud.Storage.Internal; - -namespace LeanCloud.Storage.Internal -{ - public class AppRouterController : IAppRouterController - { - private AppRouterState currentState; - private object mutex = new object(); - - /// - /// Get current app's router state - /// - /// - public AppRouterState Get() - { - if (string.IsNullOrEmpty(AVClient.CurrentConfiguration.ApplicationId)) - { - throw new AVException(AVException.ErrorCode.NotInitialized, "ApplicationId can not be null."); - } - AppRouterState state; - state = AppRouterState.GetInitial(AVClient.CurrentConfiguration.ApplicationId, AVClient.CurrentConfiguration.Region); - - lock (mutex) - { - if (currentState != null) - { - if (!currentState.isExpired()) - state = currentState; - } - } - - if (state.isExpired()) - { - lock (mutex) - { - state.FetchedAt = DateTime.Now + TimeSpan.FromMinutes(10); - } - Task.Factory.StartNew(RefreshAsync); - } - return state; - } - - public Task RefreshAsync() - { - return QueryAsync(CancellationToken.None).ContinueWith(t => - { - if (!t.IsFaulted && !t.IsCanceled) - { - lock (mutex) - { - currentState = t.Result; - } - } - }); - } - - public Task QueryAsync(CancellationToken cancellationToken) - { - string appId = AVClient.CurrentConfiguration.ApplicationId; - string url = string.Format("https://app-router.leancloud.cn/2/route?appId={0}", appId); - - return AVClient.HttpGetAsync(new Uri(url)).ContinueWith(t => - { - var tcs = new TaskCompletionSource(); - if (t.Result.Item1 != HttpStatusCode.OK) - { - tcs.SetException(new AVException(AVException.ErrorCode.ConnectionFailed, "can not reach router.", null)); - } - else - { - var result = Json.Parse(t.Result.Item2) as IDictionary; - tcs.SetResult(ParseAppRouterState(result)); - } - return tcs.Task; - }).Unwrap(); - } - - private static AppRouterState ParseAppRouterState(IDictionary jsonObj) - { - var state = new AppRouterState() - { - TTL = (int)jsonObj["ttl"], - StatsServer = jsonObj["stats_server"] as string, - RealtimeRouterServer = jsonObj["rtm_router_server"] as string, - PushServer = jsonObj["push_server"] as string, - EngineServer = jsonObj["engine_server"] as string, - ApiServer = jsonObj["api_server"] as string, - Source = "network", - }; - return state; - } - - public void Clear() - { - currentState = null; - } - } -} diff --git a/Storage/Source/Internal/AppRouter/AppRouterState.cs b/Storage/Source/Internal/AppRouter/AppRouterState.cs deleted file mode 100644 index 9b8f85d..0000000 --- a/Storage/Source/Internal/AppRouter/AppRouterState.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System; - -namespace LeanCloud.Storage.Internal -{ - public class AppRouterState - { - public long TTL { get; internal set; } - public string ApiServer { get; internal set; } - public string EngineServer { get; internal set; } - public string PushServer { get; internal set; } - public string RealtimeRouterServer { get; internal set; } - public string StatsServer { get; internal set; } - public string Source { get; internal set; } - - public DateTime FetchedAt { get; internal set; } - - private static object mutex = new object(); - - public AppRouterState() - { - FetchedAt = DateTime.Now; - } - - /// - /// Is this app router state expired. - /// - public bool isExpired() - { - return DateTime.Now > FetchedAt + TimeSpan.FromSeconds(TTL); - } - - /// - /// Get the initial usable router state - /// - /// Current app's appId - /// Current app's region - /// Initial app router state - public static AppRouterState GetInitial(string appId, AVClient.Configuration.AVRegion region) - { - var regionValue = (int)region; - var prefix = appId.Substring(0, 8).ToLower(); - switch (regionValue) - { - case 0: - // 华北 - return new AppRouterState() - { - TTL = -1, - ApiServer = string.Format("{0}.api.lncld.net", prefix), - EngineServer = string.Format("{0}.engine.lncld.net", prefix), - PushServer = string.Format("{0}.push.lncld.net", prefix), - RealtimeRouterServer = string.Format("{0}.rtm.lncld.net", prefix), - StatsServer = string.Format("{0}.stats.lncld.net", prefix), - Source = "initial", - }; - case 1: - // 美国 - return new AppRouterState() - { - TTL = -1, - ApiServer = string.Format("{0}.api.lncldglobal.com", prefix), - EngineServer = string.Format("{0}.engine.lncldglobal.com", prefix), - PushServer = string.Format("{0}.push.lncldglobal.com", prefix), - RealtimeRouterServer = string.Format("{0}.rtm.lncldglobal.com", prefix), - StatsServer = string.Format("{0}.stats.lncldglobal.com", prefix), - Source = "initial", - }; - case 2: - // 华东 - return new AppRouterState() { - TTL = -1, - ApiServer = string.Format("{0}.api.lncldapi.com", prefix), - EngineServer = string.Format("{0}.engine.lncldapi.com", prefix), - PushServer = string.Format("{0}.push.lncldapi.com", prefix), - RealtimeRouterServer = string.Format("{0}.rtm.lncldapi.com", prefix), - StatsServer = string.Format("{0}.stats.lncldapi.com", prefix), - Source = "initial", - }; - default: - throw new AVException(AVException.ErrorCode.OtherCause, "invalid region"); - } - } - - } -} \ No newline at end of file diff --git a/Storage/Source/Internal/AppRouter/IAppRouterController.cs b/Storage/Source/Internal/AppRouter/IAppRouterController.cs deleted file mode 100644 index 771ee18..0000000 --- a/Storage/Source/Internal/AppRouter/IAppRouterController.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal -{ - public interface IAppRouterController - { - AppRouterState Get(); - /// - /// Start refresh the app router. - /// - /// - Task RefreshAsync(); - void Clear(); - /// - /// Query the app router. - /// - /// New AppRouterState - Task QueryAsync(CancellationToken cancellationToken); - } -} diff --git a/Storage/Source/Internal/Authentication/IAVAuthenticationProvider.cs b/Storage/Source/Internal/Authentication/IAVAuthenticationProvider.cs deleted file mode 100644 index 406e37c..0000000 --- a/Storage/Source/Internal/Authentication/IAVAuthenticationProvider.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal { - public interface IAVAuthenticationProvider { - /// - /// Authenticates with the service. - /// - /// The cancellation token. - Task> AuthenticateAsync(CancellationToken cancellationToken); - - /// - /// Deauthenticates (logs out) the user associated with this provider. This - /// call may block. - /// - void Deauthenticate(); - - /// - /// Restores authentication that has been serialized, such as session keys, - /// etc. - /// - /// The auth data for the provider. This value may be null - /// when unlinking an account. - /// true iff the authData was successfully synchronized. A false return - /// value indicates that the user should no longer be associated because of bad auth - /// data. - bool RestoreAuthentication(IDictionary authData); - - /// - /// Provides a unique name for the type of authentication the provider does. - /// For example, the FacebookAuthenticationProvider would return "facebook". - /// - string AuthType { get; } - } -} diff --git a/Storage/Source/Internal/Cloud/Controller/AVCloudCodeController.cs b/Storage/Source/Internal/Cloud/Controller/AVCloudCodeController.cs deleted file mode 100644 index c6fa216..0000000 --- a/Storage/Source/Internal/Cloud/Controller/AVCloudCodeController.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using LeanCloud.Utilities; -using LeanCloud.Storage.Internal; - -namespace LeanCloud.Storage.Internal -{ - public class AVCloudCodeController : IAVCloudCodeController - { - private readonly IAVCommandRunner commandRunner; - - public AVCloudCodeController(IAVCommandRunner commandRunner) - { - this.commandRunner = commandRunner; - } - - public Task CallFunctionAsync(String name, - IDictionary parameters, - string sessionToken, - CancellationToken cancellationToken) - { - var command = new AVCommand(string.Format("functions/{0}", Uri.EscapeUriString(name)), - method: "POST", - sessionToken: sessionToken, - data: NoObjectsEncoder.Instance.Encode(parameters) as IDictionary); - - return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t => - { - var decoded = AVDecoder.Instance.Decode(t.Result.Item2) as IDictionary; - if (!decoded.ContainsKey("result")) - { - return default(T); - } - return Conversion.To(decoded["result"]); - }); - } - - public Task RPCFunction(string name, IDictionary parameters, string sessionToken, CancellationToken cancellationToken) - { - var command = new AVCommand(string.Format("call/{0}", Uri.EscapeUriString(name)), - method: "POST", - sessionToken: sessionToken, - data: PointerOrLocalIdEncoder.Instance.Encode(parameters) as IDictionary); - - return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t => - { - var decoded = AVDecoder.Instance.Decode(t.Result.Item2) as IDictionary; - if (!decoded.ContainsKey("result")) - { - return default(T); - } - return Conversion.To(decoded["result"]); - }); - } - } -} diff --git a/Storage/Source/Internal/Cloud/Controller/IAVCloudCodeController.cs b/Storage/Source/Internal/Cloud/Controller/IAVCloudCodeController.cs deleted file mode 100644 index 2575aae..0000000 --- a/Storage/Source/Internal/Cloud/Controller/IAVCloudCodeController.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal -{ - public interface IAVCloudCodeController - { - Task CallFunctionAsync(string name, - IDictionary parameters, - string sessionToken, - CancellationToken cancellationToken); - - Task RPCFunction(string name, IDictionary parameters, - string sessionToken, - CancellationToken cancellationToken); - } -} diff --git a/Storage/Source/Internal/Command/AVCommand.cs b/Storage/Source/Internal/Command/AVCommand.cs deleted file mode 100644 index 0d038bc..0000000 --- a/Storage/Source/Internal/Command/AVCommand.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using LeanCloud.Storage.Internal; -using System.Linq; - -namespace LeanCloud.Storage.Internal -{ - /// - /// AVCommand is an with pre-populated - /// headers. - /// - public class AVCommand : HttpRequest - { - public IDictionary DataObject { get; private set; } - public override Stream Data - { - get - { - if (base.Data != null) - { - return base.Data; - } - - return base.Data = (DataObject != null - ? new MemoryStream(Encoding.UTF8.GetBytes(Json.Encode(DataObject))) - : null); - } - set { base.Data = value; } - } - - public AVCommand(string relativeUri, - string method, - string sessionToken = null, - IList> headers = null, - IDictionary data = null) : this(relativeUri: relativeUri, - method: method, - sessionToken: sessionToken, - headers: headers, - stream: null, - contentType: data != null ? "application/json" : null) - { - DataObject = data; - } - - public AVCommand(string relativeUri, - string method, - string sessionToken = null, - IList> headers = null, - Stream stream = null, - string contentType = null) - { - var state = AVPlugins.Instance.AppRouterController.Get(); - var urlTemplate = "https://{0}/{1}/{2}"; - AVClient.Configuration configuration = AVClient.CurrentConfiguration; - var apiVersion = "1.1"; - if (relativeUri.StartsWith("push") || relativeUri.StartsWith("installations")) - { - Uri = new Uri(string.Format(urlTemplate, state.PushServer, apiVersion, relativeUri)); - if (configuration.PushServer != null) - { - Uri = new Uri(string.Format("{0}{1}/{2}", configuration.PushServer, apiVersion, relativeUri)); - } - } - else if (relativeUri.StartsWith("stats") || relativeUri.StartsWith("always_collect") || relativeUri.StartsWith("statistics")) - { - Uri = new Uri(string.Format(urlTemplate, state.StatsServer, apiVersion, relativeUri)); - if (configuration.StatsServer != null) - { - Uri = new Uri(string.Format("{0}{1}/{2}", configuration.StatsServer, apiVersion, relativeUri)); - } - } - else if (relativeUri.StartsWith("functions") || relativeUri.StartsWith("call")) - { - Uri = new Uri(string.Format(urlTemplate, state.EngineServer, apiVersion, relativeUri)); - - if (configuration.EngineServer != null) - { - Uri = new Uri(string.Format("{0}{1}/{2}", configuration.EngineServer, apiVersion, relativeUri)); - } - } - else - { - Uri = new Uri(string.Format(urlTemplate, state.ApiServer, apiVersion, relativeUri)); - - if (configuration.ApiServer != null) - { - Uri = new Uri(string.Format("{0}{1}/{2}", configuration.ApiServer, apiVersion, relativeUri)); - } - } - Method = method; - Data = stream; - Headers = new List>(headers ?? Enumerable.Empty>()); - - string useProduction = AVClient.UseProduction ? "1" : "0"; - Headers.Add(new KeyValuePair("X-LC-Prod", useProduction)); - - if (!string.IsNullOrEmpty(sessionToken)) - { - Headers.Add(new KeyValuePair("X-LC-Session", sessionToken)); - } - if (!string.IsNullOrEmpty(contentType)) - { - Headers.Add(new KeyValuePair("Content-Type", contentType)); - } - } - - public AVCommand(AVCommand other) - { - this.Uri = other.Uri; - this.Method = other.Method; - this.DataObject = other.DataObject; - this.Headers = new List>(other.Headers); - this.Data = other.Data; - } - } -} diff --git a/Storage/Source/Internal/Command/AVCommandRunner.cs b/Storage/Source/Internal/Command/AVCommandRunner.cs deleted file mode 100644 index 5ed2f4d..0000000 --- a/Storage/Source/Internal/Command/AVCommandRunner.cs +++ /dev/null @@ -1,167 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using LeanCloud.Storage.Internal; - -namespace LeanCloud.Storage.Internal -{ - /// - /// Command Runner. - /// - public class AVCommandRunner : IAVCommandRunner - { - private readonly IHttpClient httpClient; - private readonly IInstallationIdController installationIdController; - - /// - /// - /// - /// - /// - public AVCommandRunner(IHttpClient httpClient, IInstallationIdController installationIdController) - { - this.httpClient = httpClient; - this.installationIdController = installationIdController; - } - - /// - /// - /// - /// - /// - /// - /// - /// - public Task>> RunCommandAsync(AVCommand command, - IProgress uploadProgress = null, - IProgress downloadProgress = null, - CancellationToken cancellationToken = default) - { - return PrepareCommand(command).ContinueWith(commandTask => - { - var requestLog = commandTask.Result.ToLog(); - AVClient.PrintLog("http=>" + requestLog); - - return httpClient.ExecuteAsync(commandTask.Result, uploadProgress, downloadProgress, cancellationToken).OnSuccess(t => - { - cancellationToken.ThrowIfCancellationRequested(); - - var response = t.Result; - var contentString = response.Item2; - int responseCode = (int)response.Item1; - - var responseLog = responseCode + ";" + contentString; - AVClient.PrintLog("http<=" + responseLog); - - if (responseCode >= 500) - { - // Server error, return InternalServerError. - throw new AVException(AVException.ErrorCode.InternalServerError, response.Item2); - } - else if (contentString != null) - { - IDictionary contentJson = null; - try - { - if (contentString.StartsWith("[")) - { - var arrayJson = Json.Parse(contentString); - contentJson = new Dictionary { { "results", arrayJson } }; - } - else - { - contentJson = Json.Parse(contentString) as IDictionary; - } - } - catch (Exception e) - { - throw new AVException(AVException.ErrorCode.OtherCause, - "Invalid response from server", e); - } - if (responseCode < 200 || responseCode > 299) - { - AVClient.PrintLog("error response code:" + responseCode); - int code = (int)(contentJson.ContainsKey("code") ? (int)contentJson["code"] : (int)AVException.ErrorCode.OtherCause); - string error = contentJson.ContainsKey("error") ? - contentJson["error"] as string : contentString; - AVException.ErrorCode ec = (AVException.ErrorCode)code; - throw new AVException(ec, error); - } - return new Tuple>(response.Item1, - contentJson); - } - return new Tuple>(response.Item1, null); - }); - }).Unwrap(); - } - - private const string revocableSessionTokenTrueValue = "1"; - private Task PrepareCommand(AVCommand command) - { - AVCommand newCommand = new AVCommand(command); - - Task installationIdTask = installationIdController.GetAsync().ContinueWith(t => - { - newCommand.Headers.Add(new KeyValuePair("X-LC-Installation-Id", t.Result.ToString())); - return newCommand; - }); - - AVClient.Configuration configuration = AVClient.CurrentConfiguration; - newCommand.Headers.Add(new KeyValuePair("X-LC-Id", configuration.ApplicationId)); - - long timestamp = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds; - if (!string.IsNullOrEmpty(configuration.MasterKey) && AVClient.UseMasterKey) - { - string sign = MD5.GetMd5String(timestamp + configuration.MasterKey); - newCommand.Headers.Add(new KeyValuePair("X-LC-Sign", string.Format("{0},{1},master", sign, timestamp))); - } - else - { - string sign = MD5.GetMd5String(timestamp + configuration.ApplicationKey); - newCommand.Headers.Add(new KeyValuePair("X-LC-Sign", string.Format("{0},{1}", sign, timestamp))); - } - - newCommand.Headers.Add(new KeyValuePair("X-LC-Client-Version", AVClient.VersionString)); - - if (!string.IsNullOrEmpty(configuration.VersionInfo.BuildVersion)) - { - newCommand.Headers.Add(new KeyValuePair("X-LC-App-Build-Version", configuration.VersionInfo.BuildVersion)); - } - if (!string.IsNullOrEmpty(configuration.VersionInfo.DisplayVersion)) - { - newCommand.Headers.Add(new KeyValuePair("X-LC-App-Display-Version", configuration.VersionInfo.DisplayVersion)); - } - if (!string.IsNullOrEmpty(configuration.VersionInfo.OSVersion)) - { - newCommand.Headers.Add(new KeyValuePair("X-LC-OS-Version", configuration.VersionInfo.OSVersion)); - } - - if (AVUser.IsRevocableSessionEnabled) - { - newCommand.Headers.Add(new KeyValuePair("X-LeanCloud-Revocable-Session", revocableSessionTokenTrueValue)); - } - - if (configuration.AdditionalHTTPHeaders != null) - { - var headersDictionary = newCommand.Headers.ToDictionary(kv => kv.Key, kv => kv.Value); - foreach (var header in configuration.AdditionalHTTPHeaders) - { - if (headersDictionary.ContainsKey(header.Key)) - { - headersDictionary[header.Key] = header.Value; - } - else - { - newCommand.Headers.Add(header); - } - } - newCommand.Headers = headersDictionary.ToList(); - } - - return installationIdTask; - } - } -} diff --git a/Storage/Source/Internal/Command/IAVCommandRunner.cs b/Storage/Source/Internal/Command/IAVCommandRunner.cs deleted file mode 100644 index 62d5ee1..0000000 --- a/Storage/Source/Internal/Command/IAVCommandRunner.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal -{ - public interface IAVCommandRunner - { - /// - /// Executes and convert the result into Dictionary. - /// - /// The command to be run. - /// Upload progress callback. - /// Download progress callback. - /// The cancellation token for the request. - /// - Task>> RunCommandAsync(AVCommand command, - IProgress uploadProgress = null, - IProgress downloadProgress = null, - CancellationToken cancellationToken = default); - } -} diff --git a/Storage/Source/Internal/Config/Controller/AVConfigController.cs b/Storage/Source/Internal/Config/Controller/AVConfigController.cs deleted file mode 100644 index 15d34ff..0000000 --- a/Storage/Source/Internal/Config/Controller/AVConfigController.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Threading.Tasks; -using System.Threading; -using LeanCloud.Storage.Internal; - -namespace LeanCloud.Storage.Internal { - /// - /// Config controller. - /// - internal class AVConfigController : IAVConfigController { - private readonly IAVCommandRunner commandRunner; - - /// - /// Initializes a new instance of the class. - /// - public AVConfigController(IAVCommandRunner commandRunner, IStorageController storageController) { - this.commandRunner = commandRunner; - CurrentConfigController = new AVCurrentConfigController(storageController); - } - - public IAVCommandRunner CommandRunner { get; internal set; } - public IAVCurrentConfigController CurrentConfigController { get; internal set; } - - public Task FetchConfigAsync(String sessionToken, CancellationToken cancellationToken) { - var command = new AVCommand("config", - method: "GET", - sessionToken: sessionToken, - data: null); - - return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(task => { - cancellationToken.ThrowIfCancellationRequested(); - return new AVConfig(task.Result.Item2); - }).OnSuccess(task => { - cancellationToken.ThrowIfCancellationRequested(); - CurrentConfigController.SetCurrentConfigAsync(task.Result); - return task; - }).Unwrap(); - } - } -} diff --git a/Storage/Source/Internal/Config/Controller/AVCurrentConfigController.cs b/Storage/Source/Internal/Config/Controller/AVCurrentConfigController.cs deleted file mode 100644 index 41ba8d8..0000000 --- a/Storage/Source/Internal/Config/Controller/AVCurrentConfigController.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.Threading.Tasks; -using System.Threading; -using System.Collections.Generic; -using LeanCloud.Storage.Internal; - -namespace LeanCloud.Storage.Internal { - /// - /// LeanCloud current config controller. - /// - internal class AVCurrentConfigController : IAVCurrentConfigController { - private const string CurrentConfigKey = "CurrentConfig"; - - private readonly TaskQueue taskQueue; - private AVConfig currentConfig; - - private IStorageController storageController; - - /// - /// Initializes a new instance of the class. - /// - public AVCurrentConfigController(IStorageController storageController) { - this.storageController = storageController; - - taskQueue = new TaskQueue(); - } - - public Task GetCurrentConfigAsync() { - return taskQueue.Enqueue(toAwait => toAwait.ContinueWith(_ => { - if (currentConfig == null) { - return storageController.LoadAsync().OnSuccess(t => { - object tmp; - t.Result.TryGetValue(CurrentConfigKey, out tmp); - - string propertiesString = tmp as string; - if (propertiesString != null) { - var dictionary = AVClient.DeserializeJsonString(propertiesString); - currentConfig = new AVConfig(dictionary); - } else { - currentConfig = new AVConfig(); - } - - return currentConfig; - }); - } - - return Task.FromResult(currentConfig); - }), CancellationToken.None).Unwrap(); - } - - public Task SetCurrentConfigAsync(AVConfig config) { - return taskQueue.Enqueue(toAwait => toAwait.ContinueWith(_ => { - currentConfig = config; - - var jsonObject = ((IJsonConvertible)config).ToJSON(); - var jsonString = AVClient.SerializeJsonString(jsonObject); - - return storageController.LoadAsync().OnSuccess(t => t.Result.AddAsync(CurrentConfigKey, jsonString)); - }).Unwrap().Unwrap(), CancellationToken.None); - } - - public Task ClearCurrentConfigAsync() { - return taskQueue.Enqueue(toAwait => toAwait.ContinueWith(_ => { - currentConfig = null; - - return storageController.LoadAsync().OnSuccess(t => t.Result.RemoveAsync(CurrentConfigKey)); - }).Unwrap().Unwrap(), CancellationToken.None); - } - - public Task ClearCurrentConfigInMemoryAsync() { - return taskQueue.Enqueue(toAwait => toAwait.ContinueWith(_ => { - currentConfig = null; - }), CancellationToken.None); - } - } -} diff --git a/Storage/Source/Internal/Config/Controller/IAVConfigController.cs b/Storage/Source/Internal/Config/Controller/IAVConfigController.cs deleted file mode 100644 index 47cf384..0000000 --- a/Storage/Source/Internal/Config/Controller/IAVConfigController.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Threading.Tasks; -using System.Threading; - -namespace LeanCloud.Storage.Internal { - public interface IAVConfigController { - /// - /// Gets the current config controller. - /// - /// The current config controller. - IAVCurrentConfigController CurrentConfigController { get; } - - /// - /// Fetches the config from the server asynchronously. - /// - /// The config async. - /// Session token. - /// Cancellation token. - Task FetchConfigAsync(string sessionToken, CancellationToken cancellationToken); - } -} diff --git a/Storage/Source/Internal/Config/Controller/IAVCurrentConfigController.cs b/Storage/Source/Internal/Config/Controller/IAVCurrentConfigController.cs deleted file mode 100644 index 759329c..0000000 --- a/Storage/Source/Internal/Config/Controller/IAVCurrentConfigController.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal { - public interface IAVCurrentConfigController { - /// - /// Gets the current config async. - /// - /// The current config async. - Task GetCurrentConfigAsync(); - - /// - /// Sets the current config async. - /// - /// The current config async. - /// Config. - Task SetCurrentConfigAsync(AVConfig config); - - /// - /// Clears the current config async. - /// - /// The current config async. - Task ClearCurrentConfigAsync(); - - /// - /// Clears the current config in memory async. - /// - /// The current config in memory async. - Task ClearCurrentConfigInMemoryAsync(); - } -} diff --git a/Storage/Source/Internal/Dispatcher/Unity/UnityDispatcher.cs b/Storage/Source/Internal/Dispatcher/Unity/UnityDispatcher.cs deleted file mode 100644 index 3a061e2..0000000 --- a/Storage/Source/Internal/Dispatcher/Unity/UnityDispatcher.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Threading; -using UnityEngine; - -namespace LeanCloud.Storage.Internal -{ - /// - /// This class represents the internal Unity dispatcher used by the LeanCloud SDK. - /// - /// It should be initialized once in your game, usually via AVInitializeBehavior. - /// - /// In certain, advanced use-cases, you may wish to use - /// this to set up your dispatcher manually. - /// - // TODO: (richardross) Review this interface before going public. - public sealed class Dispatcher - { - static Dispatcher() - { - Instance = new Dispatcher(); - } - - private Dispatcher() - { - DispatcherCoroutine = CreateDispatcherCoroutine(); - } - - public static Dispatcher Instance { get; private set; } - - public GameObject GameObject { get; set; } - public IEnumerator DispatcherCoroutine { get; private set; } - - private readonly ReaderWriterLockSlim dispatchQueueLock = new ReaderWriterLockSlim(); - private readonly Queue dispatchQueue = new Queue(); - - public void Post(Action action) - { - if (dispatchQueueLock.IsWriteLockHeld) - { - dispatchQueue.Enqueue(action); - return; - } - - dispatchQueueLock.EnterWriteLock(); - try - { - dispatchQueue.Enqueue(action); - } - finally - { - dispatchQueueLock.ExitWriteLock(); - } - } - - private IEnumerator CreateDispatcherCoroutine() - { - // We must stop the first invocation here, so that we don't actually do anything until we begin looping. - yield return null; - while (true) - { - dispatchQueueLock.EnterUpgradeableReadLock(); - try - { - // We'll only empty what's already in the dispatch queue in this iteration (so that a - // nested dispatch behaves like nextTick()). - int count = dispatchQueue.Count; - if (count > 0) - { - dispatchQueueLock.EnterWriteLock(); - try - { - while (count > 0) - { - try - { - dispatchQueue.Dequeue()(); - } - catch (Exception e) - { - // If an exception occurs, catch it and log it so that dispatches aren't broken. - Debug.LogException(e); - } - count--; - } - } - finally - { - dispatchQueueLock.ExitWriteLock(); - } - } - } - finally - { - dispatchQueueLock.ExitUpgradeableReadLock(); - } - yield return null; - } - } - } -} diff --git a/Storage/Source/Internal/Encoding/AVDecoder.cs b/Storage/Source/Internal/Encoding/AVDecoder.cs deleted file mode 100644 index fdb4303..0000000 --- a/Storage/Source/Internal/Encoding/AVDecoder.cs +++ /dev/null @@ -1,164 +0,0 @@ -using System; -using System.Linq; -using System.Collections.Generic; -using System.Globalization; -using LeanCloud.Utilities; - -namespace LeanCloud.Storage.Internal -{ - public class AVDecoder - { - // This class isn't really a Singleton, but since it has no state, it's more efficient to get - // the default instance. - private static readonly AVDecoder instance = new AVDecoder(); - public static AVDecoder Instance - { - get - { - return instance; - } - } - - // Prevent default constructor. - private AVDecoder() { } - - public object Decode(object data) - { - if (data == null) - { - return null; - } - - var dict = data as IDictionary; - if (dict != null) - { - if (dict.ContainsKey("__op")) - { - return AVFieldOperations.Decode(dict); - } - - object type; - dict.TryGetValue("__type", out type); - var typeString = type as string; - - if (typeString == null) - { - var newDict = new Dictionary(); - foreach (var pair in dict) - { - newDict[pair.Key] = Decode(pair.Value); - } - return newDict; - } - - if (typeString == "Date") - { - return ParseDate(dict["iso"] as string); - } - - if (typeString == "Bytes") - { - return Convert.FromBase64String(dict["base64"] as string); - } - - if (typeString == "Pointer") - { - //set a include key to fetch or query. - if (dict.Keys.Count > 3) - { - return DecodeAVObject(dict); - } - return DecodePointer(dict["className"] as string, dict["objectId"] as string); - } - - if (typeString == "File") - { - return DecodeAVFile(dict); - } - - if (typeString == "GeoPoint") - { - return new AVGeoPoint(Conversion.To(dict["latitude"]), - Conversion.To(dict["longitude"])); - } - - if (typeString == "Object") - { - return DecodeAVObject(dict); - } - - if (typeString == "Relation") - { - return AVRelationBase.CreateRelation(null, null, dict["className"] as string); - } - - var converted = new Dictionary(); - foreach (var pair in dict) - { - converted[pair.Key] = Decode(pair.Value); - } - return converted; - } - - var list = data as IList; - if (list != null) - { - return (from item in list - select Decode(item)).ToList(); - } - - return data; - } - - protected virtual object DecodePointer(string className, string objectId) - { - if (className == "_File") - { - return AVFile.CreateWithoutData(objectId); - } - return AVObject.CreateWithoutData(className, objectId); - } - protected virtual object DecodeAVObject(IDictionary dict) - { - var className = dict["className"] as string; - if (className == "_File") - { - return DecodeAVFile(dict); - } - var state = AVObjectCoder.Instance.Decode(dict, this); - return AVObject.FromState(state, dict["className"] as string); - } - protected virtual object DecodeAVFile(IDictionary dict) - { - var objectId = dict["objectId"] as string; - var file = AVFile.CreateWithoutData(objectId); - file.MergeFromJSON(dict); - return file; - } - - - public virtual IList DecodeList(object data) - { - IList rtn = null; - var list = (IList)data; - if (list != null) - { - rtn = new List(); - foreach (var item in list) - { - rtn.Add((T)item); - } - } - return rtn; - } - - public static DateTime ParseDate(string input) - { - var rtn = DateTime.ParseExact(input, - AVClient.DateFormatStrings, - CultureInfo.InvariantCulture, - DateTimeStyles.AssumeUniversal); - return rtn; - } - } -} diff --git a/Storage/Source/Internal/Encoding/AVEncoder.cs b/Storage/Source/Internal/Encoding/AVEncoder.cs deleted file mode 100644 index 401edc1..0000000 --- a/Storage/Source/Internal/Encoding/AVEncoder.cs +++ /dev/null @@ -1,138 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using LeanCloud.Utilities; -using LeanCloud.Storage.Internal; - -namespace LeanCloud.Storage.Internal -{ - /// - /// A AVEncoder can be used to transform objects such as into JSON - /// data structures. - /// - /// - public abstract class AVEncoder - { -#if UNITY - private static readonly bool isCompiledByIL2CPP = AppDomain.CurrentDomain.FriendlyName.Equals("IL2CPP Root Domain"); -#else - private static readonly bool isCompiledByIL2CPP = false; -#endif - - public static bool IsValidType(object value) - { - return value == null || - ReflectionHelpers.IsPrimitive(value.GetType()) || - value is string || - value is AVObject || - value is AVACL || - value is AVFile || - value is AVGeoPoint || - value is AVRelationBase || - value is DateTime || - value is byte[] || - Conversion.As>(value) != null || - Conversion.As>(value) != null; - } - - public object Encode(object value) - { - // If this object has a special encoding, encode it and return the - // encoded object. Otherwise, just return the original object. - if (value is DateTime) - { - return new Dictionary - { - { - "iso", ((DateTime)value).ToUniversalTime().ToString(AVClient.DateFormatStrings.First(), CultureInfo.InvariantCulture) - }, - { - "__type", "Date" - } - }; - } - - if (value is AVFile) - { - var file = value as AVFile; - return new Dictionary - { - {"__type", "Pointer"}, - { "className", "_File"}, - { "objectId", file.ObjectId} - }; - } - - var bytes = value as byte[]; - if (bytes != null) - { - return new Dictionary - { - { "__type", "Bytes"}, - { "base64", Convert.ToBase64String(bytes)} - }; - } - - var obj = value as AVObject; - if (obj != null) - { - return EncodeAVObject(obj); - } - - var jsonConvertible = value as IJsonConvertible; - if (jsonConvertible != null) - { - return jsonConvertible.ToJSON(); - } - - var dict = Conversion.As>(value); - if (dict != null) - { - var json = new Dictionary(); - foreach (var pair in dict) - { - json[pair.Key] = Encode(pair.Value); - } - return json; - } - - var list = Conversion.As>(value); - if (list != null) - { - return EncodeList(list); - } - - // TODO (hallucinogen): convert IAVFieldOperation to IJsonConvertible - var operation = value as IAVFieldOperation; - if (operation != null) - { - return operation.Encode(); - } - - return value; - } - - protected abstract IDictionary EncodeAVObject(AVObject value); - - private object EncodeList(IList list) - { - var newArray = new List(); - // We need to explicitly cast `list` to `List` rather than - // `IList` because IL2CPP is stricter than the usual Unity AOT compiler pipeline. - if (isCompiledByIL2CPP && list.GetType().IsArray) - { - list = new List(list); - } - foreach (var item in list) - { - if (!IsValidType(item)) - { - throw new ArgumentException("Invalid type for value in an array"); - } - newArray.Add(Encode(item)); - } - return newArray; - } - } -} diff --git a/Storage/Source/Internal/Encoding/AVObjectCoder.cs b/Storage/Source/Internal/Encoding/AVObjectCoder.cs deleted file mode 100644 index 05a6509..0000000 --- a/Storage/Source/Internal/Encoding/AVObjectCoder.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace LeanCloud.Storage.Internal -{ - // TODO: (richardross) refactor entire LeanCloud coder interfaces. - public class AVObjectCoder - { - private static readonly AVObjectCoder instance = new AVObjectCoder(); - public static AVObjectCoder Instance - { - get - { - return instance; - } - } - - // Prevent default constructor. - private AVObjectCoder() { } - - public IDictionary Encode(T state, - IDictionary operations, - AVEncoder encoder) where T : IObjectState - { - var result = new Dictionary(); - foreach (var pair in operations) - { - // AVRPCSerialize the data - var operation = pair.Value; - - result[pair.Key] = encoder.Encode(operation); - } - - return result; - } - - public IObjectState Decode(IDictionary data, - AVDecoder decoder) - { - IDictionary serverData = new Dictionary(); - var mutableData = new Dictionary(data); - string objectId = extractFromDictionary(mutableData, "objectId", (obj) => - { - return obj as string; - }); - DateTime? createdAt = extractFromDictionary(mutableData, "createdAt", (obj) => - { - return AVDecoder.ParseDate(obj as string); - }); - DateTime? updatedAt = extractFromDictionary(mutableData, "updatedAt", (obj) => - { - return AVDecoder.ParseDate(obj as string); - }); - - if (mutableData.ContainsKey("ACL")) - { - serverData["ACL"] = extractFromDictionary(mutableData, "ACL", (obj) => - { - return new AVACL(obj as IDictionary); - }); - } - string className = extractFromDictionary(mutableData, "className", obj => - { - return obj as string; - }); - if (createdAt != null && updatedAt == null) - { - updatedAt = createdAt; - } - - // Bring in the new server data. - foreach (var pair in mutableData) - { - if (pair.Key == "__type" || pair.Key == "className") - { - continue; - } - - var value = pair.Value; - serverData[pair.Key] = decoder.Decode(value); - } - - return new MutableObjectState - { - ObjectId = objectId, - CreatedAt = createdAt, - UpdatedAt = updatedAt, - ServerData = serverData, - ClassName = className - }; - } - - private T extractFromDictionary(IDictionary data, string key, Func action) - { - T result = default(T); - if (data.ContainsKey(key)) - { - result = action(data[key]); - data.Remove(key); - } - - return result; - } - } -} diff --git a/Storage/Source/Internal/Encoding/NoObjectsEncoder.cs b/Storage/Source/Internal/Encoding/NoObjectsEncoder.cs deleted file mode 100644 index 6a1e7e1..0000000 --- a/Storage/Source/Internal/Encoding/NoObjectsEncoder.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace LeanCloud.Storage.Internal { - /// - /// A that throws an exception if it attempts to encode - /// a - /// - public class NoObjectsEncoder : AVEncoder { - // This class isn't really a Singleton, but since it has no state, it's more efficient to get - // the default instance. - private static readonly NoObjectsEncoder instance = new NoObjectsEncoder(); - public static NoObjectsEncoder Instance { - get { - return instance; - } - } - - protected override IDictionary EncodeAVObject(AVObject value) { - throw new ArgumentException("AVObjects not allowed here."); - } - } -} diff --git a/Storage/Source/Internal/Encoding/PointerOrLocalIdEncoder.cs b/Storage/Source/Internal/Encoding/PointerOrLocalIdEncoder.cs deleted file mode 100644 index 2394cf7..0000000 --- a/Storage/Source/Internal/Encoding/PointerOrLocalIdEncoder.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; - -namespace LeanCloud.Storage.Internal -{ - /// - /// A that encode as pointers. If the object - /// does not have an , uses a local id. - /// - public class PointerOrLocalIdEncoder : AVEncoder - { - // This class isn't really a Singleton, but since it has no state, it's more efficient to get - // the default instance. - private static readonly PointerOrLocalIdEncoder instance = new PointerOrLocalIdEncoder(); - public static PointerOrLocalIdEncoder Instance - { - get - { - return instance; - } - } - - protected override IDictionary EncodeAVObject(AVObject value) - { - if (value.ObjectId == null) - { - // TODO (hallucinogen): handle local id. For now we throw. - throw new ArgumentException("Cannot create a pointer to an object without an objectId"); - } - - return new Dictionary { - {"__type", "Pointer"}, - { "className", value.ClassName}, - { "objectId", value.ObjectId} - }; - } - - public IDictionary EncodeAVObject(AVObject value, bool isPointer) - { - if (isPointer) - { - return EncodeAVObject(value); - } - var operations = value.GetCurrentOperations(); - var operationJSON = AVObject.ToJSONObjectForSaving(operations); - var objectJSON = value.ToDictionary(kvp => kvp.Key, kvp => PointerOrLocalIdEncoder.Instance.Encode(kvp.Value)); - foreach (var kvp in operationJSON) - { - objectJSON[kvp.Key] = kvp.Value; - } - if (value.CreatedAt.HasValue) - { - objectJSON["createdAt"] = value.CreatedAt.Value.ToString(AVClient.DateFormatStrings.First(), - CultureInfo.InvariantCulture); - } - if (value.UpdatedAt.HasValue) - { - objectJSON["updatedAt"] = value.UpdatedAt.Value.ToString(AVClient.DateFormatStrings.First(), - CultureInfo.InvariantCulture); - } - if(!string.IsNullOrEmpty(value.ObjectId)) - { - objectJSON["objectId"] = value.ObjectId; - } - objectJSON["className"] = value.ClassName; - objectJSON["__type"] = "Object"; - return objectJSON; - } - } -} diff --git a/Storage/Source/Internal/File/Controller/AVFileController.cs b/Storage/Source/Internal/File/Controller/AVFileController.cs deleted file mode 100644 index 192d64c..0000000 --- a/Storage/Source/Internal/File/Controller/AVFileController.cs +++ /dev/null @@ -1,133 +0,0 @@ -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using LeanCloud.Storage.Internal; -using System.Net; -using System.Collections.Generic; -using System.Linq; - -namespace LeanCloud.Storage.Internal { - /// - /// AVF ile controller. - /// - public class AVFileController : IAVFileController { - private readonly IAVCommandRunner commandRunner; - /// - /// Initializes a new instance of the class. - /// - /// Command runner. - public AVFileController(IAVCommandRunner commandRunner) { - this.commandRunner = commandRunner; - } - /// - /// Saves the async. - /// - /// The async. - /// State. - /// Data stream. - /// Session token. - /// Progress. - /// Cancellation token. - public virtual Task SaveAsync(FileState state, - Stream dataStream, - string sessionToken, - IProgress progress, - CancellationToken cancellationToken = default) { - if (state.Url != null) { - // !isDirty - return Task.FromResult(state); - } - - if (cancellationToken.IsCancellationRequested) { - var tcs = new TaskCompletionSource(); - tcs.TrySetCanceled(); - return tcs.Task; - } - - var oldPosition = dataStream.Position; - var command = new AVCommand("files/" + state.Name, - method: "POST", - sessionToken: sessionToken, - contentType: state.MimeType, - stream: dataStream); - - return commandRunner.RunCommandAsync(command, - uploadProgress: progress, - cancellationToken: cancellationToken).OnSuccess(uploadTask => { - var result = uploadTask.Result; - var jsonData = result.Item2; - cancellationToken.ThrowIfCancellationRequested(); - - return new FileState { - Name = jsonData["name"] as string, - Url = new Uri(jsonData["url"] as string, UriKind.Absolute), - MimeType = state.MimeType - }; - }).ContinueWith(t => { - // Rewind the stream on failure or cancellation (if possible) - if ((t.IsFaulted || t.IsCanceled) && dataStream.CanSeek) { - dataStream.Seek(oldPosition, SeekOrigin.Begin); - } - return t; - }).Unwrap(); - } - public Task DeleteAsync(FileState state, string sessionToken, CancellationToken cancellationToken) { - var command = new AVCommand("files/" + state.ObjectId, - method: "DELETE", - sessionToken: sessionToken, - data: null); - - return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken); - } - internal static Task>> GetFileToken(FileState fileState, CancellationToken cancellationToken) { - Task>> rtn; - string currentSessionToken = AVUser.CurrentSessionToken; - string str = fileState.Name; - IDictionary parameters = new Dictionary(); - parameters.Add("name", str); - parameters.Add("key", GetUniqueName(fileState)); - parameters.Add("__type", "File"); - parameters.Add("mime_type", AVFile.GetMIMEType(str)); - parameters.Add("metaData", fileState.MetaData); - - rtn = AVClient.RequestAsync("POST", new Uri("fileTokens", UriKind.Relative), currentSessionToken, parameters, cancellationToken); - - return rtn; - } - public Task GetAsync(string objectId, string sessionToken, CancellationToken cancellationToken) { - var command = new AVCommand("files/" + objectId, - method: "GET", - sessionToken: sessionToken, - data: null); - - return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(_ => { - var result = _.Result; - var jsonData = result.Item2; - cancellationToken.ThrowIfCancellationRequested(); - return new FileState { - ObjectId = jsonData["objectId"] as string, - Name = jsonData["name"] as string, - Url = new Uri(jsonData["url"] as string, UriKind.Absolute), - }; - }); - } - internal static string GetUniqueName(FileState fileState) { - string key = Random(12); - string extension = Path.GetExtension(fileState.Name); - key += extension; - fileState.CloudName = key; - return key; - } - internal static string Random(int length) { - const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"; - var random = new Random(); - return new string(Enumerable.Repeat(chars, length) - .Select(s => s[random.Next(s.Length)]).ToArray()); - } - internal static double CalcProgress(double already, double total) { - var pv = (1.0 * already / total); - return Math.Round(pv, 3); - } - } -} diff --git a/Storage/Source/Internal/File/Controller/AWSS3FileController.cs b/Storage/Source/Internal/File/Controller/AWSS3FileController.cs deleted file mode 100644 index 313b82c..0000000 --- a/Storage/Source/Internal/File/Controller/AWSS3FileController.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Threading.Tasks; -using System.Threading; -using System.IO; -using LeanCloud.Storage.Internal; -using System.Collections.Generic; - -namespace LeanCloud.Storage.Internal -{ - internal class AWSS3FileController : AVFileController - { - - private object mutex = new object(); - - - public AWSS3FileController(IAVCommandRunner commandRunner) : base(commandRunner) - { - - } - - public override Task SaveAsync(FileState state, Stream dataStream, string sessionToken, IProgress progress, CancellationToken cancellationToken = default) - { - if (state.Url != null) - { - return Task.FromResult(state); - } - - return GetFileToken(state, cancellationToken).OnSuccess(t => - { - var fileToken = t.Result.Item2; - var uploadUrl = fileToken["upload_url"].ToString(); - state.ObjectId = fileToken["objectId"].ToString(); - string url = fileToken["url"] as string; - state.Url = new Uri(url, UriKind.Absolute); - return PutFile(state, uploadUrl, dataStream); - - }).Unwrap().OnSuccess(s => - { - return s.Result; - }); - } - - internal Task PutFile(FileState state, string uploadUrl, Stream dataStream) - { - IList> makeBlockHeaders = new List>(); - makeBlockHeaders.Add(new KeyValuePair("Content-Type", state.MimeType)); - - return AVClient.RequestAsync(new Uri(uploadUrl), "PUT", makeBlockHeaders, dataStream, state.MimeType, CancellationToken.None).OnSuccess(t => - { - return state; - }); - } - } -} diff --git a/Storage/Source/Internal/File/Controller/IAVFileController.cs b/Storage/Source/Internal/File/Controller/IAVFileController.cs deleted file mode 100644 index 305f51e..0000000 --- a/Storage/Source/Internal/File/Controller/IAVFileController.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal -{ - public interface IAVFileController - { - Task SaveAsync(FileState state, - Stream dataStream, - string sessionToken, - IProgress progress, - CancellationToken cancellationToken); - - Task DeleteAsync(FileState state, - string sessionToken, - CancellationToken cancellationToken); - - Task GetAsync(string objectId, - string sessionToken, - CancellationToken cancellationToken); - } -} diff --git a/Storage/Source/Internal/File/Controller/QCloudCosFileController.cs b/Storage/Source/Internal/File/Controller/QCloudCosFileController.cs deleted file mode 100644 index 9eee34a..0000000 --- a/Storage/Source/Internal/File/Controller/QCloudCosFileController.cs +++ /dev/null @@ -1,250 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Linq; -using LeanCloud.Storage.Internal; - -namespace LeanCloud.Storage.Internal -{ - internal class QCloudCosFileController : AVFileController - { - private object mutex = new object(); - - FileState fileState; - Stream data; - string bucket; - string token; - string uploadUrl; - bool done; - private long sliceSize = (long)CommonSize.KB512; - - public QCloudCosFileController(IAVCommandRunner commandRunner) : base(commandRunner) - { - } - - public Task SaveAsync(FileState state, - Stream dataStream, - string sessionToken, - IProgress progress, - CancellationToken cancellationToken) - { - if (state.Url != null) - { - return Task.FromResult(state); - } - fileState = state; - data = dataStream; - return GetFileToken(fileState, cancellationToken).OnSuccess(_ => - { - var fileToken = _.Result.Item2; - uploadUrl = fileToken["upload_url"].ToString(); - token = fileToken["token"].ToString(); - fileState.ObjectId = fileToken["objectId"].ToString(); - bucket = fileToken["bucket"].ToString(); - - return FileSlice(cancellationToken).OnSuccess(t => - { - if (done) return Task.FromResult(state); - var response = t.Result.Item2; - var resumeData = response["data"] as IDictionary; - if (resumeData.ContainsKey("access_url")) return Task.FromResult(state); - var sliceSession = resumeData["session"].ToString(); - var sliceOffset = long.Parse(resumeData["offset"].ToString()); - return UploadSlice(sliceSession, sliceOffset, dataStream, progress, cancellationToken); - }).Unwrap(); - - }).Unwrap(); - } - - Task UploadSlice( - string sessionId, - long offset, - Stream dataStream, - IProgress progress, - CancellationToken cancellationToken) - { - - long dataLength = dataStream.Length; - if (progress != null) - { - lock (mutex) - { - progress.Report(new AVUploadProgressEventArgs() - { - Progress = AVFileController.CalcProgress(offset, dataLength) - }); - } - } - - if (offset == dataLength) - { - return Task.FromResult(fileState); - } - - var sliceFile = GetNextBinary(offset, dataStream); - return ExcuteUpload(sessionId, offset, sliceFile, cancellationToken).OnSuccess(_ => - { - offset += sliceFile.Length; - if (offset == dataLength) - { - done = true; - return Task.FromResult(fileState); - } - var response = _.Result.Item2; - var resumeData = response["data"] as IDictionary; - var sliceSession = resumeData["session"].ToString(); - return UploadSlice(sliceSession, offset, dataStream, progress, cancellationToken); - }).Unwrap(); - } - - Task>> ExcuteUpload(string sessionId, long offset, byte[] sliceFile, CancellationToken cancellationToken) - { - var body = new Dictionary(); - body.Add("op", "upload_slice"); - body.Add("session", sessionId); - body.Add("offset", offset.ToString()); - - return PostToQCloud(body, sliceFile, cancellationToken); - } - - Task>> FileSlice(CancellationToken cancellationToken) - { - SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider(); - var body = new Dictionary(); - if (data.Length <= (long)CommonSize.KB512) - { - body.Add("op", "upload"); - body.Add("sha", HexStringFromBytes(sha1.ComputeHash(data))); - var wholeFile = GetNextBinary(0, data); - return PostToQCloud(body, wholeFile, cancellationToken).OnSuccess(_ => - { - if (_.Result.Item1 == HttpStatusCode.OK) - { - done = true; - } - return _.Result; - }); - } - else - { - body.Add("op", "upload_slice"); - body.Add("filesize", data.Length); - body.Add("sha", HexStringFromBytes(sha1.ComputeHash(data))); - body.Add("slice_size", (long)CommonSize.KB512); - } - - return PostToQCloud(body, null, cancellationToken); - } - public static string HexStringFromBytes(byte[] bytes) - { - var sb = new StringBuilder(); - foreach (byte b in bytes) - { - var hex = b.ToString("x2"); - sb.Append(hex); - } - return sb.ToString(); - } - - public static string SHA1HashStringForUTF8String(string s) - { - byte[] bytes = Encoding.UTF8.GetBytes(s); - - SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider(); - byte[] hashBytes = sha1.ComputeHash(bytes); - - return HexStringFromBytes(hashBytes); - } - Task>> PostToQCloud( - Dictionary body, - byte[] sliceFile, - CancellationToken cancellationToken) - { - IList> sliceHeaders = new List>(); - sliceHeaders.Add(new KeyValuePair("Authorization", this.token)); - - string contentType; - long contentLength; - - var tempStream = HttpUploadFile(sliceFile, fileState.CloudName, out contentType, out contentLength, body); - - sliceHeaders.Add(new KeyValuePair("Content-Type", contentType)); - - var rtn = AVClient.RequestAsync(new Uri(this.uploadUrl), "POST", sliceHeaders, tempStream, null, cancellationToken).OnSuccess(_ => - { - var dic = AVClient.ReponseResolve(_.Result, CancellationToken.None); - - return dic; - }); - - return rtn; - } - public static Stream HttpUploadFile(byte[] file, string fileName, out string contentType, out long contentLength, IDictionary nvc) - { - string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x"); - byte[] boundarybytes = StringToAscii("\r\n--" + boundary + "\r\n"); - contentType = "multipart/form-data; boundary=" + boundary; - - MemoryStream rs = new MemoryStream(); - - string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}"; - foreach (string key in nvc.Keys) - { - rs.Write(boundarybytes, 0, boundarybytes.Length); - string formitem = string.Format(formdataTemplate, key, nvc[key]); - byte[] formitembytes = System.Text.Encoding.UTF8.GetBytes(formitem); - rs.Write(formitembytes, 0, formitembytes.Length); - } - rs.Write(boundarybytes, 0, boundarybytes.Length); - - if (file != null) - { - string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n"; - string header = string.Format(headerTemplate, "fileContent", fileName, "application/octet-stream"); - byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes(header); - rs.Write(headerbytes, 0, headerbytes.Length); - - rs.Write(file, 0, file.Length); - } - - byte[] trailer = StringToAscii("\r\n--" + boundary + "--\r\n"); - rs.Write(trailer, 0, trailer.Length); - contentLength = rs.Length; - - rs.Position = 0; - var tempBuffer = new byte[rs.Length]; - rs.Read(tempBuffer, 0, tempBuffer.Length); - - return new MemoryStream(tempBuffer); - } - - public static byte[] StringToAscii(string s) - { - byte[] retval = new byte[s.Length]; - for (int ix = 0; ix < s.Length; ++ix) - { - char ch = s[ix]; - if (ch <= 0x7f) retval[ix] = (byte)ch; - else retval[ix] = (byte)'?'; - } - return retval; - } - - byte[] GetNextBinary(long completed, Stream dataStream) - { - if (completed + sliceSize > dataStream.Length) - { - sliceSize = dataStream.Length - completed; - } - - byte[] chunkBinary = new byte[sliceSize]; - dataStream.Seek(completed, SeekOrigin.Begin); - dataStream.Read(chunkBinary, 0, (int)sliceSize); - return chunkBinary; - } - } -} diff --git a/Storage/Source/Internal/File/Controller/QiniuFileController.cs b/Storage/Source/Internal/File/Controller/QiniuFileController.cs deleted file mode 100644 index 72a8c5b..0000000 --- a/Storage/Source/Internal/File/Controller/QiniuFileController.cs +++ /dev/null @@ -1,332 +0,0 @@ -using LeanCloud.Storage.Internal; -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal -{ - internal enum CommonSize : long - { - MB4 = 1024 * 1024 * 4, - MB1 = 1024 * 1024, - KB512 = 1024 * 1024 / 2, - KB256 = 1024 * 1024 / 4 - } - - internal class QiniuFileController : AVFileController - { - private static int BLOCKSIZE = 1024 * 1024 * 4; - private const int blockMashk = (1 << blockBits) - 1; - private const int blockBits = 22; - private int CalcBlockCount(long fsize) - { - return (int)((fsize + blockMashk) >> blockBits); - } - internal static string UP_HOST = "https://up.qbox.me"; - private object mutex = new object(); - - public QiniuFileController(IAVCommandRunner commandRunner) : base(commandRunner) - { - - } - - public override Task SaveAsync(FileState state, - Stream dataStream, - String sessionToken, - IProgress progress, - CancellationToken cancellationToken) - { - if (state.Url != null) - { - return Task.FromResult(state); - } - state.frozenData = dataStream; - state.CloudName = GetUniqueName(state); - return GetQiniuToken(state, CancellationToken.None).ContinueWith(t => - { - MergeFromJSON(state, t.Result.Item2); - return UploadNextChunk(state, dataStream, string.Empty, 0, progress); - }).Unwrap().OnSuccess(s => - { - return state; - }); - } - Task UploadNextChunk(FileState state, Stream dataStream, string context, long offset, IProgress progress) - { - var totalSize = dataStream.Length; - var remainingSize = totalSize - state.completed; - - if (progress != null) - { - lock (mutex) - { - progress.Report(new AVUploadProgressEventArgs() - { - Progress = AVFileController.CalcProgress(state.completed, totalSize) - }); - } - } - if (state.completed == totalSize) - { - return QiniuMakeFile(state, state.frozenData, state.token, state.CloudName, totalSize, state.block_ctxes.ToArray(), CancellationToken.None); - - } - else if (state.completed % BLOCKSIZE == 0) - { - var firstChunkBinary = GetChunkBinary(state.completed, dataStream); - - var blockSize = remainingSize > BLOCKSIZE ? BLOCKSIZE : remainingSize; - return MakeBlock(state, firstChunkBinary, blockSize).ContinueWith(t => - { - - var dic = AVClient.ReponseResolve(t.Result, CancellationToken.None); - var ctx = dic.Item2["ctx"].ToString(); - - offset = long.Parse(dic.Item2["offset"].ToString()); - var host = dic.Item2["host"].ToString(); - - state.completed += firstChunkBinary.Length; - if (state.completed % BLOCKSIZE == 0 || state.completed == totalSize) - { - state.block_ctxes.Add(ctx); - } - - return UploadNextChunk(state, dataStream, ctx, offset, progress); - }).Unwrap(); - - } - else - { - var chunkBinary = GetChunkBinary(state.completed, dataStream); - return PutChunk(state, chunkBinary, context, offset).ContinueWith(t => - { - var dic = AVClient.ReponseResolve(t.Result, CancellationToken.None); - var ctx = dic.Item2["ctx"].ToString(); - - offset = long.Parse(dic.Item2["offset"].ToString()); - var host = dic.Item2["host"].ToString(); - state.completed += chunkBinary.Length; - if (state.completed % BLOCKSIZE == 0 || state.completed == totalSize) - { - state.block_ctxes.Add(ctx); - } - //if (AVClient.fileUploaderDebugLog) - //{ - // AVClient.LogTracker(state.counter + "|completed=" + state.completed + "stream:position=" + dataStream.Position + "|"); - //} - - return UploadNextChunk(state, dataStream, ctx, offset, progress); - }).Unwrap(); - } - } - - byte[] GetChunkBinary(long completed, Stream dataStream) - { - long chunkSize = (long)CommonSize.MB1; - if (completed + chunkSize > dataStream.Length) - { - chunkSize = dataStream.Length - completed; - } - byte[] chunkBinary = new byte[chunkSize]; - dataStream.Seek(completed, SeekOrigin.Begin); - dataStream.Read(chunkBinary, 0, (int)chunkSize); - return chunkBinary; - } - - internal string GetUniqueName(FileState state) - { - string key = Guid.NewGuid().ToString();//file Key in Qiniu. - string extension = Path.GetExtension(state.Name); - key += extension; - return key; - } - internal Task>> GetQiniuToken(FileState state, CancellationToken cancellationToken) - { - Task>> rtn; - string currentSessionToken = AVUser.CurrentSessionToken; - string str = state.Name; - - IDictionary parameters = new Dictionary(); - parameters.Add("name", str); - parameters.Add("key", state.CloudName); - parameters.Add("__type", "File"); - parameters.Add("mime_type", AVFile.GetMIMEType(str)); - - state.MetaData = GetMetaData(state, state.frozenData); - - parameters.Add("metaData", state.MetaData); - - rtn = AVClient.RequestAsync("POST", new Uri("qiniu", UriKind.Relative), currentSessionToken, parameters, cancellationToken); - - return rtn; - } - IList> GetQiniuRequestHeaders(FileState state) - { - IList> makeBlockHeaders = new List>(); - - string authHead = "UpToken " + state.token; - makeBlockHeaders.Add(new KeyValuePair("Authorization", authHead)); - return makeBlockHeaders; - } - - Task> MakeBlock(FileState state, byte[] firstChunkBinary, long blcokSize = 4194304) - { - MemoryStream firstChunkData = new MemoryStream(firstChunkBinary, 0, firstChunkBinary.Length); - return AVClient.RequestAsync(new Uri(new Uri(UP_HOST) + string.Format("mkblk/{0}", blcokSize)), "POST", GetQiniuRequestHeaders(state), firstChunkData, "application/octet-stream", CancellationToken.None); - } - Task> PutChunk(FileState state, byte[] chunkBinary, string LastChunkctx, long currentChunkOffsetInBlock) - { - MemoryStream chunkData = new MemoryStream(chunkBinary, 0, chunkBinary.Length); - return AVClient.RequestAsync(new Uri(new Uri(UP_HOST) + string.Format("bput/{0}/{1}", LastChunkctx, - currentChunkOffsetInBlock)), "POST", - GetQiniuRequestHeaders(state), chunkData, - "application/octet-stream", CancellationToken.None); - } - internal Task> QiniuMakeFile(FileState state, Stream dataStream, string upToken, string key, long fsize, string[] ctxes, CancellationToken cancellationToken) - { - StringBuilder urlBuilder = new StringBuilder(); - urlBuilder.AppendFormat("{0}/mkfile/{1}", UP_HOST, fsize); - if (key != null) - { - urlBuilder.AppendFormat("/key/{0}", ToBase64URLSafe(key)); - } - var metaData = GetMetaData(state, dataStream); - - StringBuilder sb = new StringBuilder(); - foreach (string _key in metaData.Keys) - { - sb.AppendFormat("/{0}/{1}", _key, ToBase64URLSafe(metaData[_key].ToString())); - } - urlBuilder.Append(sb.ToString()); - - IList> headers = new List>(); - //makeBlockDic.Add("Content-Type", "application/octet-stream"); - - string authHead = "UpToken " + upToken; - headers.Add(new KeyValuePair("Authorization", authHead)); - int proCount = ctxes.Length; - Stream body = new MemoryStream(); - - for (int i = 0; i < proCount; i++) - { - byte[] bctx = StringToAscii(ctxes[i]); - body.Write(bctx, 0, bctx.Length); - if (i != proCount - 1) - { - body.WriteByte((byte)','); - } - } - body.Seek(0, SeekOrigin.Begin); - - var rtn = AVClient.RequestAsync(new Uri(urlBuilder.ToString()), "POST", headers, body, "text/plain", cancellationToken).OnSuccess(_ => - { - var dic = AVClient.ReponseResolve(_.Result, CancellationToken.None); - return _.Result; - }); - return rtn; - } - internal void MergeFromJSON(FileState state, IDictionary jsonData) - { - lock (this.mutex) - { - string url = jsonData["url"] as string; - state.Url = new Uri(url, UriKind.Absolute); - state.bucketId = FetchBucketId(url); - state.token = jsonData["token"] as string; - state.bucket = jsonData["bucket"] as string; - state.ObjectId = jsonData["objectId"] as string; - } - } - - string FetchBucketId(string url) - { - var elements = url.Split('/'); - - return elements[elements.Length - 1]; - } - public static byte[] StringToAscii(string s) - { - byte[] retval = new byte[s.Length]; - for (int ix = 0; ix < s.Length; ++ix) - { - char ch = s[ix]; - if (ch <= 0x7f) - retval[ix] = (byte)ch; - else - retval[ix] = (byte)'?'; - } - return retval; - } - public static string ToBase64URLSafe(string str) - { - return Encode(str); - } - public static string Encode(byte[] bs) - { - if (bs == null || bs.Length == 0) - return ""; - string encodedStr = Convert.ToBase64String(bs); - encodedStr = encodedStr.Replace('+', '-').Replace('/', '_'); - return encodedStr; - } - public static string Encode(string text) - { - if (String.IsNullOrEmpty(text)) - return ""; - byte[] bs = Encoding.UTF8.GetBytes(text); - string encodedStr = Convert.ToBase64String(bs); - encodedStr = encodedStr.Replace('+', '-').Replace('/', '_'); - return encodedStr; - } - - internal static string GetMD5Code(Stream data) - { - MD5 md5 = new MD5CryptoServiceProvider(); - byte[] retVal = md5.ComputeHash(data); - - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < retVal.Length; i++) - { - sb.Append(retVal[i].ToString("x2")); - } - return sb.ToString(); - - } - - internal IDictionary GetMetaData(FileState state, Stream data) - { - IDictionary rtn = new Dictionary(); - - if (state.MetaData != null) - { - foreach (var meta in state.MetaData) - { - rtn.Add(meta.Key, meta.Value); - } - } - MergeDic(rtn, "mime_type", AVFile.GetMIMEType(state.Name)); - MergeDic(rtn, "size", data.Length); - MergeDic(rtn, "_checksum", GetMD5Code(data)); - if (AVUser.CurrentUser != null) - if (AVUser.CurrentUser.ObjectId != null) - MergeDic(rtn, "owner", AVUser.CurrentUser.ObjectId); - - return rtn; - } - internal void MergeDic(IDictionary dic, string key, object value) - { - if (dic.ContainsKey(key)) - { - dic[key] = value; - } - else - { - dic.Add(key, value); - } - } - } -} diff --git a/Storage/Source/Internal/File/Cryptography/MD5/MD5.cs b/Storage/Source/Internal/File/Cryptography/MD5/MD5.cs deleted file mode 100644 index aa3eb2b..0000000 --- a/Storage/Source/Internal/File/Cryptography/MD5/MD5.cs +++ /dev/null @@ -1,566 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.IO; - -namespace LeanCloud.Storage.Internal -{ - /// - /// 弥补Windows Phone 8 API没有自带MD5加密的拓展方法。 - /// - internal class MD5CryptoServiceProvider : MD5 - { - public MD5CryptoServiceProvider() - : base() - { - } - } - /// - /// Summary description for MD5. - /// - internal class MD5 : IDisposable - { - static public MD5 Create(string hashName) - { - if (hashName == "MD5") - return new MD5(); - else - throw new NotSupportedException(); - } - - static public string GetMd5String(String source) - { - MD5 md = MD5CryptoServiceProvider.Create(); - byte[] hash; - - //Create a new instance of ASCIIEncoding to - //convert the string into an array of Unicode bytes. - UTF8Encoding enc = new UTF8Encoding(); - // ASCIIEncoding enc = new ASCIIEncoding(); - - //Convert the string into an array of bytes. - byte[] buffer = enc.GetBytes(source); - - //Create the hash value from the array of bytes. - hash = md.ComputeHash(buffer); - - StringBuilder sb = new StringBuilder(); - foreach (byte b in hash) - sb.Append(b.ToString("x2")); - return sb.ToString(); - } - - static public MD5 Create() - { - return new MD5(); - } - - #region base implementation of the MD5 - #region constants - private const byte S11 = 7; - private const byte S12 = 12; - private const byte S13 = 17; - private const byte S14 = 22; - private const byte S21 = 5; - private const byte S22 = 9; - private const byte S23 = 14; - private const byte S24 = 20; - private const byte S31 = 4; - private const byte S32 = 11; - private const byte S33 = 16; - private const byte S34 = 23; - private const byte S41 = 6; - private const byte S42 = 10; - private const byte S43 = 15; - private const byte S44 = 21; - static private byte[] PADDING = new byte[] { - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - #endregion - - #region F, G, H and I are basic MD5 functions. - static private uint F(uint x, uint y, uint z) - { - return (((x) & (y)) | ((~x) & (z))); - } - static private uint G(uint x, uint y, uint z) - { - return (((x) & (z)) | ((y) & (~z))); - } - static private uint H(uint x, uint y, uint z) - { - return ((x) ^ (y) ^ (z)); - } - static private uint I(uint x, uint y, uint z) - { - return ((y) ^ ((x) | (~z))); - } - #endregion - - #region rotates x left n bits. - /// - /// rotates x left n bits. - /// - /// - /// - /// - static private uint ROTATE_LEFT(uint x, byte n) - { - return (((x) << (n)) | ((x) >> (32 - (n)))); - } - #endregion - - #region FF, GG, HH, and II transformations - /// FF, GG, HH, and II transformations - /// for rounds 1, 2, 3, and 4. - /// Rotation is separate from addition to prevent recomputation. - static private void FF(ref uint a, uint b, uint c, uint d, uint x, byte s, uint ac) - { - (a) += F((b), (c), (d)) + (x) + (uint)(ac); - (a) = ROTATE_LEFT((a), (s)); - (a) += (b); - } - static private void GG(ref uint a, uint b, uint c, uint d, uint x, byte s, uint ac) - { - (a) += G((b), (c), (d)) + (x) + (uint)(ac); - (a) = ROTATE_LEFT((a), (s)); - (a) += (b); - } - static private void HH(ref uint a, uint b, uint c, uint d, uint x, byte s, uint ac) - { - (a) += H((b), (c), (d)) + (x) + (uint)(ac); - (a) = ROTATE_LEFT((a), (s)); - (a) += (b); - } - static private void II(ref uint a, uint b, uint c, uint d, uint x, byte s, uint ac) - { - (a) += I((b), (c), (d)) + (x) + (uint)(ac); - (a) = ROTATE_LEFT((a), (s)); - (a) += (b); - } - #endregion - - #region context info - /// - /// state (ABCD) - /// - uint[] state = new uint[4]; - - /// - /// number of bits, modulo 2^64 (lsb first) - /// - uint[] count = new uint[2]; - - /// - /// input buffer - /// - byte[] buffer = new byte[64]; - #endregion - - internal MD5() - { - Initialize(); - } - - /// - /// MD5 initialization. Begins an MD5 operation, writing a new context. - /// - /// - /// The RFC named it "MD5Init" - /// - public virtual void Initialize() - { - count[0] = count[1] = 0; - - // Load magic initialization constants. - state[0] = 0x67452301; - state[1] = 0xefcdab89; - state[2] = 0x98badcfe; - state[3] = 0x10325476; - } - - /// - /// MD5 block update operation. Continues an MD5 message-digest - /// operation, processing another message block, and updating the - /// context. - /// - /// - /// - /// - /// The RFC Named it MD5Update - protected virtual void HashCore(byte[] input, int offset, int count) - { - int i; - int index; - int partLen; - - // Compute number of bytes mod 64 - index = (int)((this.count[0] >> 3) & 0x3F); - - // Update number of bits - if ((this.count[0] += (uint)((uint)count << 3)) < ((uint)count << 3)) - this.count[1]++; - this.count[1] += ((uint)count >> 29); - - partLen = 64 - index; - - // Transform as many times as possible. - if (count >= partLen) - { - Buffer.BlockCopy(input, offset, this.buffer, index, partLen); - Transform(this.buffer, 0); - - for (i = partLen; i + 63 < count; i += 64) - Transform(input, offset + i); - - index = 0; - } - else - i = 0; - - // Buffer remaining input - Buffer.BlockCopy(input, offset + i, this.buffer, index, count - i); - } - - /// - /// MD5 finalization. Ends an MD5 message-digest operation, writing the - /// the message digest and zeroizing the context. - /// - /// message digest - /// The RFC named it MD5Final - protected virtual byte[] HashFinal() - { - byte[] digest = new byte[16]; - byte[] bits = new byte[8]; - int index, padLen; - - // Save number of bits - Encode(bits, 0, this.count, 0, 8); - - // Pad out to 56 mod 64. - index = (int)((uint)(this.count[0] >> 3) & 0x3f); - padLen = (index < 56) ? (56 - index) : (120 - index); - HashCore(PADDING, 0, padLen); - - // Append length (before padding) - HashCore(bits, 0, 8); - - // Store state in digest - Encode(digest, 0, state, 0, 16); - - // Zeroize sensitive information. - count[0] = count[1] = 0; - state[0] = 0; - state[1] = 0; - state[2] = 0; - state[3] = 0; - - // initialize again, to be ready to use - Initialize(); - - return digest; - } - - /// - /// MD5 basic transformation. Transforms state based on 64 bytes block. - /// - /// - /// - private void Transform(byte[] block, int offset) - { - uint a = state[0], b = state[1], c = state[2], d = state[3]; - uint[] x = new uint[16]; - Decode(x, 0, block, offset, 64); - - // Round 1 - FF(ref a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */ - FF(ref d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */ - FF(ref c, d, a, b, x[2], S13, 0x242070db); /* 3 */ - FF(ref b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */ - FF(ref a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */ - FF(ref d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */ - FF(ref c, d, a, b, x[6], S13, 0xa8304613); /* 7 */ - FF(ref b, c, d, a, x[7], S14, 0xfd469501); /* 8 */ - FF(ref a, b, c, d, x[8], S11, 0x698098d8); /* 9 */ - FF(ref d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */ - FF(ref c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ - FF(ref b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ - FF(ref a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ - FF(ref d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ - FF(ref c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ - FF(ref b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ - - // Round 2 - GG(ref a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */ - GG(ref d, a, b, c, x[6], S22, 0xc040b340); /* 18 */ - GG(ref c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ - GG(ref b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */ - GG(ref a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */ - GG(ref d, a, b, c, x[10], S22, 0x2441453); /* 22 */ - GG(ref c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ - GG(ref b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */ - GG(ref a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */ - GG(ref d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ - GG(ref c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */ - GG(ref b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */ - GG(ref a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ - GG(ref d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */ - GG(ref c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */ - GG(ref b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ - - // Round 3 - HH(ref a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */ - HH(ref d, a, b, c, x[8], S32, 0x8771f681); /* 34 */ - HH(ref c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ - HH(ref b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ - HH(ref a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */ - HH(ref d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */ - HH(ref c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */ - HH(ref b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ - HH(ref a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ - HH(ref d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */ - HH(ref c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */ - HH(ref b, c, d, a, x[6], S34, 0x4881d05); /* 44 */ - HH(ref a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */ - HH(ref d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ - HH(ref c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ - HH(ref b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */ - - // Round 4 - II(ref a, b, c, d, x[0], S41, 0xf4292244); /* 49 */ - II(ref d, a, b, c, x[7], S42, 0x432aff97); /* 50 */ - II(ref c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ - II(ref b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */ - II(ref a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ - II(ref d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */ - II(ref c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ - II(ref b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */ - II(ref a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */ - II(ref d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ - II(ref c, d, a, b, x[6], S43, 0xa3014314); /* 59 */ - II(ref b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ - II(ref a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */ - II(ref d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ - II(ref c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */ - II(ref b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */ - - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - - // Zeroize sensitive information. - for (int i = 0; i < x.Length; i++) - x[i] = 0; - } - - /// - /// Encodes input (uint) into output (byte). Assumes len is - /// multiple of 4. - /// - /// - /// - /// - /// - /// - private static void Encode(byte[] output, int outputOffset, uint[] input, int inputOffset, int count) - { - int i, j; - int end = outputOffset + count; - for (i = inputOffset, j = outputOffset; j < end; i++, j += 4) - { - output[j] = (byte)(input[i] & 0xff); - output[j + 1] = (byte)((input[i] >> 8) & 0xff); - output[j + 2] = (byte)((input[i] >> 16) & 0xff); - output[j + 3] = (byte)((input[i] >> 24) & 0xff); - } - } - - /// - /// Decodes input (byte) into output (uint). Assumes len is - /// a multiple of 4. - /// - /// - /// - /// - /// - /// - static private void Decode(uint[] output, int outputOffset, byte[] input, int inputOffset, int count) - { - int i, j; - int end = inputOffset + count; - for (i = outputOffset, j = inputOffset; j < end; i++, j += 4) - output[i] = ((uint)input[j]) | (((uint)input[j + 1]) << 8) | (((uint)input[j + 2]) << 16) | (((uint)input[j + 3]) << 24); - } - #endregion - - #region expose the same interface as the regular MD5 object - - protected byte[] HashValue; - protected int State; - public virtual bool CanReuseTransform - { - get - { - return true; - } - } - - public virtual bool CanTransformMultipleBlocks - { - get - { - return true; - } - } - public virtual byte[] Hash - { - get - { - if (this.State != 0) - throw new InvalidOperationException(); - return (byte[])HashValue.Clone(); - } - } - public virtual int HashSize - { - get - { - return HashSizeValue; - } - } - protected int HashSizeValue = 128; - - public virtual int InputBlockSize - { - get - { - return 1; - } - } - public virtual int OutputBlockSize - { - get - { - return 1; - } - } - - public void Clear() - { - Dispose(true); - } - - public byte[] ComputeHash(byte[] buffer) - { - return ComputeHash(buffer, 0, buffer.Length); - } - public byte[] ComputeHash(byte[] buffer, int offset, int count) - { - Initialize(); - HashCore(buffer, offset, count); - HashValue = HashFinal(); - return (byte[])HashValue.Clone(); - } - - public byte[] ComputeHash(Stream inputStream) - { - Initialize(); - int count; - byte[] buffer = new byte[4096]; - while (0 < (count = inputStream.Read(buffer, 0, 4096))) - { - HashCore(buffer, 0, count); - } - HashValue = HashFinal(); - return (byte[])HashValue.Clone(); - } - - public int TransformBlock( - byte[] inputBuffer, - int inputOffset, - int inputCount, - byte[] outputBuffer, - int outputOffset - ) - { - if (inputBuffer == null) - { - throw new ArgumentNullException("inputBuffer"); - } - if (inputOffset < 0) - { - throw new ArgumentOutOfRangeException("inputOffset"); - } - if ((inputCount < 0) || (inputCount > inputBuffer.Length)) - { - throw new ArgumentException("inputCount"); - } - if ((inputBuffer.Length - inputCount) < inputOffset) - { - throw new ArgumentOutOfRangeException("inputOffset"); - } - if (this.State == 0) - { - Initialize(); - this.State = 1; - } - - HashCore(inputBuffer, inputOffset, inputCount); - if ((inputBuffer != outputBuffer) || (inputOffset != outputOffset)) - { - Buffer.BlockCopy(inputBuffer, inputOffset, outputBuffer, outputOffset, inputCount); - } - return inputCount; - } - public byte[] TransformFinalBlock( - byte[] inputBuffer, - int inputOffset, - int inputCount - ) - { - if (inputBuffer == null) - { - throw new ArgumentNullException("inputBuffer"); - } - if (inputOffset < 0) - { - throw new ArgumentOutOfRangeException("inputOffset"); - } - if ((inputCount < 0) || (inputCount > inputBuffer.Length)) - { - throw new ArgumentException("inputCount"); - } - if ((inputBuffer.Length - inputCount) < inputOffset) - { - throw new ArgumentOutOfRangeException("inputOffset"); - } - if (this.State == 0) - { - Initialize(); - } - HashCore(inputBuffer, inputOffset, inputCount); - HashValue = HashFinal(); - byte[] buffer = new byte[inputCount]; - Buffer.BlockCopy(inputBuffer, inputOffset, buffer, 0, inputCount); - this.State = 0; - return buffer; - } - #endregion - - protected virtual void Dispose(bool disposing) - { - if (!disposing) - Initialize(); - } - public void Dispose() - { - Dispose(true); - } - } -} - diff --git a/Storage/Source/Internal/File/Cryptography/SHA1/SHA1CryptoServiceProvider.cs b/Storage/Source/Internal/File/Cryptography/SHA1/SHA1CryptoServiceProvider.cs deleted file mode 100644 index 74f6e09..0000000 --- a/Storage/Source/Internal/File/Cryptography/SHA1/SHA1CryptoServiceProvider.cs +++ /dev/null @@ -1,495 +0,0 @@ -// -// System.Security.Cryptography.SHA1CryptoServiceProvider.cs -// -// Authors: -// Matthew S. Ford (Matthew.S.Ford@Rose-Hulman.Edu) -// Sebastien Pouliot (sebastien@ximian.com) -// -// Copyright 2001 by Matthew S. Ford. -// Copyright (C) 2004, 2005, 2008 Novell, Inc (http://www.novell.com) -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -// Note: -// The MS Framework includes two (almost) identical class for SHA1. -// SHA1Managed is a 100% managed implementation. -// SHA1CryptoServiceProvider (this file) is a wrapper on CryptoAPI. -// Mono must provide those two class for binary compatibility. -// In our case both class are wrappers around a managed internal class SHA1Internal. - -using System.IO; -using System; -using System.Runtime.InteropServices; - -namespace LeanCloud.Storage.Internal -{ - - internal class SHA1Internal - { - - private const int BLOCK_SIZE_BYTES = 64; - private uint[] _H; // these are my chaining variables - private ulong count; - private byte[] _ProcessingBuffer; // Used to start data when passed less than a block worth. - private int _ProcessingBufferCount; // Counts how much data we have stored that still needs processed. - private uint[] buff; - - public SHA1Internal() - { - _H = new uint[5]; - _ProcessingBuffer = new byte[BLOCK_SIZE_BYTES]; - buff = new uint[80]; - - Initialize(); - } - - public void HashCore(byte[] rgb, int ibStart, int cbSize) - { - int i; - - if (_ProcessingBufferCount != 0) - { - if (cbSize < (BLOCK_SIZE_BYTES - _ProcessingBufferCount)) - { - System.Buffer.BlockCopy(rgb, ibStart, _ProcessingBuffer, _ProcessingBufferCount, cbSize); - _ProcessingBufferCount += cbSize; - return; - } - else - { - i = (BLOCK_SIZE_BYTES - _ProcessingBufferCount); - System.Buffer.BlockCopy(rgb, ibStart, _ProcessingBuffer, _ProcessingBufferCount, i); - ProcessBlock(_ProcessingBuffer, 0); - _ProcessingBufferCount = 0; - ibStart += i; - cbSize -= i; - } - } - - for (i = 0; i < cbSize - cbSize % BLOCK_SIZE_BYTES; i += BLOCK_SIZE_BYTES) - { - ProcessBlock(rgb, (uint)(ibStart + i)); - } - - if (cbSize % BLOCK_SIZE_BYTES != 0) - { - System.Buffer.BlockCopy(rgb, cbSize - cbSize % BLOCK_SIZE_BYTES + ibStart, _ProcessingBuffer, 0, cbSize % BLOCK_SIZE_BYTES); - _ProcessingBufferCount = cbSize % BLOCK_SIZE_BYTES; - } - } - - public byte[] HashFinal() - { - byte[] hash = new byte[20]; - - ProcessFinalBlock(_ProcessingBuffer, 0, _ProcessingBufferCount); - - for (int i = 0; i < 5; i++) - { - for (int j = 0; j < 4; j++) - { - hash[i * 4 + j] = (byte)(_H[i] >> (8 * (3 - j))); - } - } - - return hash; - } - - public void Initialize() - { - count = 0; - _ProcessingBufferCount = 0; - - _H[0] = 0x67452301; - _H[1] = 0xefcdab89; - _H[2] = 0x98badcfe; - _H[3] = 0x10325476; - _H[4] = 0xC3D2E1F0; - } - - private void ProcessBlock(byte[] inputBuffer, uint inputOffset) - { - uint a, b, c, d, e; - - count += BLOCK_SIZE_BYTES; - - // abc removal would not work on the fields - uint[] _H = this._H; - uint[] buff = this.buff; - InitialiseBuff(buff, inputBuffer, inputOffset); - FillBuff(buff); - - a = _H[0]; - b = _H[1]; - c = _H[2]; - d = _H[3]; - e = _H[4]; - - // This function was unrolled because it seems to be doubling our performance with current compiler/VM. - // Possibly roll up if this changes. - - // ---- Round 1 -------- - int i = 0; - while (i < 20) - { - e += ((a << 5) | (a >> 27)) + (((c ^ d) & b) ^ d) + 0x5A827999 + buff[i]; - b = (b << 30) | (b >> 2); - - d += ((e << 5) | (e >> 27)) + (((b ^ c) & a) ^ c) + 0x5A827999 + buff[i + 1]; - a = (a << 30) | (a >> 2); - - c += ((d << 5) | (d >> 27)) + (((a ^ b) & e) ^ b) + 0x5A827999 + buff[i + 2]; - e = (e << 30) | (e >> 2); - - b += ((c << 5) | (c >> 27)) + (((e ^ a) & d) ^ a) + 0x5A827999 + buff[i + 3]; - d = (d << 30) | (d >> 2); - - a += ((b << 5) | (b >> 27)) + (((d ^ e) & c) ^ e) + 0x5A827999 + buff[i + 4]; - c = (c << 30) | (c >> 2); - i += 5; - } - - // ---- Round 2 -------- - while (i < 40) - { - e += ((a << 5) | (a >> 27)) + (b ^ c ^ d) + 0x6ED9EBA1 + buff[i]; - b = (b << 30) | (b >> 2); - - d += ((e << 5) | (e >> 27)) + (a ^ b ^ c) + 0x6ED9EBA1 + buff[i + 1]; - a = (a << 30) | (a >> 2); - - c += ((d << 5) | (d >> 27)) + (e ^ a ^ b) + 0x6ED9EBA1 + buff[i + 2]; - e = (e << 30) | (e >> 2); - - b += ((c << 5) | (c >> 27)) + (d ^ e ^ a) + 0x6ED9EBA1 + buff[i + 3]; - d = (d << 30) | (d >> 2); - - a += ((b << 5) | (b >> 27)) + (c ^ d ^ e) + 0x6ED9EBA1 + buff[i + 4]; - c = (c << 30) | (c >> 2); - i += 5; - } - - // ---- Round 3 -------- - while (i < 60) - { - e += ((a << 5) | (a >> 27)) + ((b & c) | (b & d) | (c & d)) + 0x8F1BBCDC + buff[i]; - b = (b << 30) | (b >> 2); - - d += ((e << 5) | (e >> 27)) + ((a & b) | (a & c) | (b & c)) + 0x8F1BBCDC + buff[i + 1]; - a = (a << 30) | (a >> 2); - - c += ((d << 5) | (d >> 27)) + ((e & a) | (e & b) | (a & b)) + 0x8F1BBCDC + buff[i + 2]; - e = (e << 30) | (e >> 2); - - b += ((c << 5) | (c >> 27)) + ((d & e) | (d & a) | (e & a)) + 0x8F1BBCDC + buff[i + 3]; - d = (d << 30) | (d >> 2); - - a += ((b << 5) | (b >> 27)) + ((c & d) | (c & e) | (d & e)) + 0x8F1BBCDC + buff[i + 4]; - c = (c << 30) | (c >> 2); - i += 5; - } - - // ---- Round 4 -------- - while (i < 80) - { - e += ((a << 5) | (a >> 27)) + (b ^ c ^ d) + 0xCA62C1D6 + buff[i]; - b = (b << 30) | (b >> 2); - - d += ((e << 5) | (e >> 27)) + (a ^ b ^ c) + 0xCA62C1D6 + buff[i + 1]; - a = (a << 30) | (a >> 2); - - c += ((d << 5) | (d >> 27)) + (e ^ a ^ b) + 0xCA62C1D6 + buff[i + 2]; - e = (e << 30) | (e >> 2); - - b += ((c << 5) | (c >> 27)) + (d ^ e ^ a) + 0xCA62C1D6 + buff[i + 3]; - d = (d << 30) | (d >> 2); - - a += ((b << 5) | (b >> 27)) + (c ^ d ^ e) + 0xCA62C1D6 + buff[i + 4]; - c = (c << 30) | (c >> 2); - i += 5; - } - - _H[0] += a; - _H[1] += b; - _H[2] += c; - _H[3] += d; - _H[4] += e; - } - - private static void InitialiseBuff(uint[] buff, byte[] input, uint inputOffset) - { - buff[0] = (uint)((input[inputOffset + 0] << 24) | (input[inputOffset + 1] << 16) | (input[inputOffset + 2] << 8) | (input[inputOffset + 3])); - buff[1] = (uint)((input[inputOffset + 4] << 24) | (input[inputOffset + 5] << 16) | (input[inputOffset + 6] << 8) | (input[inputOffset + 7])); - buff[2] = (uint)((input[inputOffset + 8] << 24) | (input[inputOffset + 9] << 16) | (input[inputOffset + 10] << 8) | (input[inputOffset + 11])); - buff[3] = (uint)((input[inputOffset + 12] << 24) | (input[inputOffset + 13] << 16) | (input[inputOffset + 14] << 8) | (input[inputOffset + 15])); - buff[4] = (uint)((input[inputOffset + 16] << 24) | (input[inputOffset + 17] << 16) | (input[inputOffset + 18] << 8) | (input[inputOffset + 19])); - buff[5] = (uint)((input[inputOffset + 20] << 24) | (input[inputOffset + 21] << 16) | (input[inputOffset + 22] << 8) | (input[inputOffset + 23])); - buff[6] = (uint)((input[inputOffset + 24] << 24) | (input[inputOffset + 25] << 16) | (input[inputOffset + 26] << 8) | (input[inputOffset + 27])); - buff[7] = (uint)((input[inputOffset + 28] << 24) | (input[inputOffset + 29] << 16) | (input[inputOffset + 30] << 8) | (input[inputOffset + 31])); - buff[8] = (uint)((input[inputOffset + 32] << 24) | (input[inputOffset + 33] << 16) | (input[inputOffset + 34] << 8) | (input[inputOffset + 35])); - buff[9] = (uint)((input[inputOffset + 36] << 24) | (input[inputOffset + 37] << 16) | (input[inputOffset + 38] << 8) | (input[inputOffset + 39])); - buff[10] = (uint)((input[inputOffset + 40] << 24) | (input[inputOffset + 41] << 16) | (input[inputOffset + 42] << 8) | (input[inputOffset + 43])); - buff[11] = (uint)((input[inputOffset + 44] << 24) | (input[inputOffset + 45] << 16) | (input[inputOffset + 46] << 8) | (input[inputOffset + 47])); - buff[12] = (uint)((input[inputOffset + 48] << 24) | (input[inputOffset + 49] << 16) | (input[inputOffset + 50] << 8) | (input[inputOffset + 51])); - buff[13] = (uint)((input[inputOffset + 52] << 24) | (input[inputOffset + 53] << 16) | (input[inputOffset + 54] << 8) | (input[inputOffset + 55])); - buff[14] = (uint)((input[inputOffset + 56] << 24) | (input[inputOffset + 57] << 16) | (input[inputOffset + 58] << 8) | (input[inputOffset + 59])); - buff[15] = (uint)((input[inputOffset + 60] << 24) | (input[inputOffset + 61] << 16) | (input[inputOffset + 62] << 8) | (input[inputOffset + 63])); - } - - private static void FillBuff(uint[] buff) - { - uint val; - for (int i = 16; i < 80; i += 8) - { - val = buff[i - 3] ^ buff[i - 8] ^ buff[i - 14] ^ buff[i - 16]; - buff[i] = (val << 1) | (val >> 31); - - val = buff[i - 2] ^ buff[i - 7] ^ buff[i - 13] ^ buff[i - 15]; - buff[i + 1] = (val << 1) | (val >> 31); - - val = buff[i - 1] ^ buff[i - 6] ^ buff[i - 12] ^ buff[i - 14]; - buff[i + 2] = (val << 1) | (val >> 31); - - val = buff[i + 0] ^ buff[i - 5] ^ buff[i - 11] ^ buff[i - 13]; - buff[i + 3] = (val << 1) | (val >> 31); - - val = buff[i + 1] ^ buff[i - 4] ^ buff[i - 10] ^ buff[i - 12]; - buff[i + 4] = (val << 1) | (val >> 31); - - val = buff[i + 2] ^ buff[i - 3] ^ buff[i - 9] ^ buff[i - 11]; - buff[i + 5] = (val << 1) | (val >> 31); - - val = buff[i + 3] ^ buff[i - 2] ^ buff[i - 8] ^ buff[i - 10]; - buff[i + 6] = (val << 1) | (val >> 31); - - val = buff[i + 4] ^ buff[i - 1] ^ buff[i - 7] ^ buff[i - 9]; - buff[i + 7] = (val << 1) | (val >> 31); - } - } - - private void ProcessFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) - { - ulong total = count + (ulong)inputCount; - int paddingSize = (56 - (int)(total % BLOCK_SIZE_BYTES)); - - if (paddingSize < 1) - paddingSize += BLOCK_SIZE_BYTES; - - int length = inputCount + paddingSize + 8; - byte[] fooBuffer = (length == 64) ? _ProcessingBuffer : new byte[length]; - - for (int i = 0; i < inputCount; i++) - { - fooBuffer[i] = inputBuffer[i + inputOffset]; - } - - fooBuffer[inputCount] = 0x80; - for (int i = inputCount + 1; i < inputCount + paddingSize; i++) - { - fooBuffer[i] = 0x00; - } - - // I deal in bytes. The algorithm deals in bits. - ulong size = total << 3; - AddLength(size, fooBuffer, inputCount + paddingSize); - ProcessBlock(fooBuffer, 0); - - if (length == 128) - ProcessBlock(fooBuffer, 64); - } - - internal void AddLength(ulong length, byte[] buffer, int position) - { - buffer[position++] = (byte)(length >> 56); - buffer[position++] = (byte)(length >> 48); - buffer[position++] = (byte)(length >> 40); - buffer[position++] = (byte)(length >> 32); - buffer[position++] = (byte)(length >> 24); - buffer[position++] = (byte)(length >> 16); - buffer[position++] = (byte)(length >> 8); - buffer[position] = (byte)(length); - } - } - - public sealed class SHA1CryptoServiceProvider : SHA1 - { - - private SHA1Internal sha; - - public SHA1CryptoServiceProvider() - { - sha = new SHA1Internal(); - } - - ~SHA1CryptoServiceProvider() - { - Dispose(false); - } - - protected override void Dispose(bool disposing) - { - // nothing new to do (managed implementation) - base.Dispose(disposing); - } - - protected override void HashCore(byte[] rgb, int ibStart, int cbSize) - { - State = 1; - sha.HashCore(rgb, ibStart, cbSize); - } - - protected override byte[] HashFinal() - { - State = 0; - return sha.HashFinal(); - } - - public override void Initialize() - { - sha.Initialize(); - } - } - - public abstract class SHA1 : HashAlgorithm - { - protected SHA1() - { - HashSizeValue = 160; - } - } - - public abstract class HashAlgorithm : IDisposable - { - protected int HashSizeValue; - protected internal byte[] HashValue; - protected int State = 0; - - private bool m_bDisposed = false; - - protected HashAlgorithm() { } - - // - // public properties - // - - public virtual int HashSize - { - get { return HashSizeValue; } - } - - // - // public methods - // - - public byte[] ComputeHash(Stream inputStream) - { - if (m_bDisposed) - throw new ObjectDisposedException(null); - - // Default the buffer size to 4K. - byte[] buffer = new byte[4096]; - int bytesRead; - do - { - bytesRead = inputStream.Read(buffer, 0, 4096); - if (bytesRead > 0) - { - HashCore(buffer, 0, bytesRead); - } - } while (bytesRead > 0); - - HashValue = HashFinal(); - byte[] Tmp = (byte[])HashValue.Clone(); - Initialize(); - return (Tmp); - } - - public byte[] ComputeHash(byte[] buffer) - { - if (m_bDisposed) - throw new ObjectDisposedException(null); - - // Do some validation - if (buffer == null) throw new ArgumentNullException("buffer"); - - HashCore(buffer, 0, buffer.Length); - HashValue = HashFinal(); - byte[] Tmp = (byte[])HashValue.Clone(); - Initialize(); - return (Tmp); - } - - // ICryptoTransform methods - - // we assume any HashAlgorithm can take input a byte at a time - public virtual int InputBlockSize - { - get { return (1); } - } - - public virtual int OutputBlockSize - { - get { return (1); } - } - - public virtual bool CanTransformMultipleBlocks - { - get { return (true); } - } - - public virtual bool CanReuseTransform - { - get { return (true); } - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - public void Clear() - { - (this as IDisposable).Dispose(); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - if (HashValue != null) - Array.Clear(HashValue, 0, HashValue.Length); - HashValue = null; - m_bDisposed = true; - } - } - - // - // abstract public methods - // - - public abstract void Initialize(); - - protected abstract void HashCore(byte[] array, int ibStart, int cbSize); - - protected abstract byte[] HashFinal(); - } -} diff --git a/Storage/Source/Internal/File/State/FileState.cs b/Storage/Source/Internal/File/State/FileState.cs deleted file mode 100644 index 667646a..0000000 --- a/Storage/Source/Internal/File/State/FileState.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; - -namespace LeanCloud.Storage.Internal -{ - public class FileState - { - public string ObjectId { get; internal set; } - public string Name { get; internal set; } - public string CloudName { get; set; } - public string MimeType { get; internal set; } - public Uri Url { get; internal set; } - public IDictionary MetaData { get; internal set; } - public long Size { get; internal set; } - public long FixedChunkSize { get; internal set; } - - public int counter; - public Stream frozenData; - public string bucketId; - public string bucket; - public string token; - public long completed; - public List block_ctxes = new List(); - - } -} diff --git a/Storage/Source/Internal/HttpClient/HttpRequest.cs b/Storage/Source/Internal/HttpClient/HttpRequest.cs deleted file mode 100644 index f3e1444..0000000 --- a/Storage/Source/Internal/HttpClient/HttpRequest.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; - -namespace LeanCloud.Storage.Internal -{ - /// - /// IHttpRequest is an interface that provides an API to execute HTTP request data. - /// - public class HttpRequest - { - public Uri Uri { get; set; } - public IList> Headers { get; set; } - - /// - /// Data stream to be uploaded. - /// - public virtual Stream Data { get; set; } - - /// - /// HTTP method. One of DELETE, GET, HEAD, POST or PUT - /// - public string Method { get; set; } - } -} diff --git a/Storage/Source/Internal/HttpClient/IHttpClient.cs b/Storage/Source/Internal/HttpClient/IHttpClient.cs deleted file mode 100644 index 6bbe9d2..0000000 --- a/Storage/Source/Internal/HttpClient/IHttpClient.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Net; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal -{ - public interface IHttpClient - { - /// - /// Executes HTTP request to a with HTTP verb - /// and . - /// - /// The HTTP request to be executed. - /// Upload progress callback. - /// Download progress callback. - /// The cancellation token. - /// A task that resolves to Htt - Task> ExecuteAsync(HttpRequest httpRequest, - IProgress uploadProgress, - IProgress downloadProgress, - CancellationToken cancellationToken); - } -} diff --git a/Storage/Source/Internal/HttpClient/Portable/HttpClient.Portable.cs b/Storage/Source/Internal/HttpClient/Portable/HttpClient.Portable.cs deleted file mode 100644 index 210b66f..0000000 --- a/Storage/Source/Internal/HttpClient/Portable/HttpClient.Portable.cs +++ /dev/null @@ -1,163 +0,0 @@ -using System; -using System.IO; -using System.Net; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Net.Http; -using System.Linq; - -using NetHttpClient = System.Net.Http.HttpClient; -using System.Net.Http.Headers; -using System.Collections.Generic; - -namespace LeanCloud.Storage.Internal -{ - public class HttpClient : IHttpClient - { - private static HashSet HttpContentHeaders = new HashSet { - { "Allow" }, - { "Content-Disposition" }, - { "Content-Encoding" }, - { "Content-Language" }, - { "Content-Length" }, - { "Content-Location" }, - { "Content-MD5" }, - { "Content-Range" }, - { "Content-Type" }, - { "Expires" }, - { "Last-Modified" } - }; - - public HttpClient() - { - client = new NetHttpClient(); - client.DefaultRequestHeaders.Add("User-Agent", "LeanCloud-dotNet-SDK/" + "2.0.0"); - } - - public HttpClient(NetHttpClient client) - { - this.client = client; - } - - private NetHttpClient client; - - public Task> ExecuteAsync(HttpRequest httpRequest, - IProgress uploadProgress, - IProgress downloadProgress, - CancellationToken cancellationToken) - { - uploadProgress = uploadProgress ?? new Progress(); - downloadProgress = downloadProgress ?? new Progress(); - - HttpMethod httpMethod = new HttpMethod(httpRequest.Method); - HttpRequestMessage message = new HttpRequestMessage(httpMethod, httpRequest.Uri); - - // Fill in zero-length data if method is post. - Stream data = httpRequest.Data; - if (httpRequest.Data == null && httpRequest.Method.ToLower().Equals("post")) - { - data = new MemoryStream(new byte[0]); - } - - if (data != null) - { - message.Content = new StreamContent(data); - } - - if (httpRequest.Headers != null) - { - foreach (var header in httpRequest.Headers) - { - if (!string.IsNullOrEmpty(header.Value)) - { - if (HttpContentHeaders.Contains(header.Key)) - { - message.Content.Headers.Add(header.Key, header.Value); - } - else - { - message.Headers.Add(header.Key, header.Value); - } - } - } - } - - // Avoid aggressive caching on Windows Phone 8.1. - message.Headers.Add("Cache-Control", "no-cache"); - message.Headers.IfModifiedSince = DateTimeOffset.UtcNow; - - // TODO: (richardross) investigate progress here, maybe there's something we're missing in order to support this. - uploadProgress.Report(new AVUploadProgressEventArgs { Progress = 0 }); - - return client.SendAsync(message, HttpCompletionOption.ResponseHeadersRead, cancellationToken) - .ContinueWith(httpMessageTask => - { - var response = httpMessageTask.Result; - - uploadProgress.Report(new AVUploadProgressEventArgs { Progress = 1 }); - - return response.Content.ReadAsStreamAsync().ContinueWith(streamTask => - { - var resultStream = new MemoryStream(); - var responseStream = streamTask.Result; - - int bufferSize = 4096; - byte[] buffer = new byte[bufferSize]; - int bytesRead = 0; - long totalLength = -1; - long readSoFar = 0; - - try - { - totalLength = responseStream.Length; - } - catch (NotSupportedException) - { - - } - - return InternalExtensions.WhileAsync(() => - { - return responseStream.ReadAsync(buffer, 0, bufferSize, cancellationToken).OnSuccess(readTask => - { - bytesRead = readTask.Result; - return bytesRead > 0; - }); - }, () => - { - cancellationToken.ThrowIfCancellationRequested(); - - return resultStream.WriteAsync(buffer, 0, bytesRead, cancellationToken).OnSuccess(_ => - { - cancellationToken.ThrowIfCancellationRequested(); - readSoFar += bytesRead; - - if (totalLength > -1) - { - downloadProgress.Report(new AVDownloadProgressEventArgs { Progress = 1.0 * readSoFar / totalLength }); - } - }); - }).ContinueWith(_ => - { - responseStream.Dispose(); - return _; - }).Unwrap().OnSuccess(_ => - { - // If getting stream size is not supported, then report download only once. - if (totalLength == -1) - { - downloadProgress.Report(new AVDownloadProgressEventArgs { Progress = 1.0 }); - } - - // Assume UTF-8 encoding. - var resultAsArray = resultStream.ToArray(); - var resultString = Encoding.UTF8.GetString(resultAsArray, 0, resultAsArray.Length); - resultStream.Dispose(); - return new Tuple(response.StatusCode, resultString); - }); - }); - }).Unwrap().Unwrap(); - } - } -} diff --git a/Storage/Source/Internal/HttpClient/Unity/HttpClient.Unity.cs b/Storage/Source/Internal/HttpClient/Unity/HttpClient.Unity.cs deleted file mode 100644 index 39b2671..0000000 --- a/Storage/Source/Internal/HttpClient/Unity/HttpClient.Unity.cs +++ /dev/null @@ -1,168 +0,0 @@ -using System; -using System.IO; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using UnityEngine; -using UnityEngine.Networking; - -namespace LeanCloud.Storage.Internal -{ - public class HttpClient : IHttpClient - { - private static bool isCompiledByIL2CPP = System.AppDomain.CurrentDomain.FriendlyName.Equals("IL2CPP Root Domain"); - - public Task> ExecuteAsync(HttpRequest httpRequest, - IProgress uploadProgress, - IProgress downloadProgress, - CancellationToken cancellationToken) - { - var tcs = new TaskCompletionSource>(); - cancellationToken.Register(() => tcs.TrySetCanceled()); - uploadProgress = uploadProgress ?? new Progress(); - downloadProgress = downloadProgress ?? new Progress(); - - Task readBytesTask = null; - IDisposable toDisposeAfterReading = null; - byte[] bytes = null; - if (httpRequest.Data != null) - { - var ms = new MemoryStream(); - toDisposeAfterReading = ms; - readBytesTask = httpRequest.Data.CopyToAsync(ms).OnSuccess(_ => - { - bytes = ms.ToArray(); - }); - } - - readBytesTask.Safe().ContinueWith(t => - { - if (toDisposeAfterReading != null) - { - toDisposeAfterReading.Dispose(); - } - return t; - }).Unwrap().OnSuccess(_ => - { - float oldDownloadProgress = 0; - float oldUploadProgress = 0; - - Dispatcher.Instance.Post(() => - { - WaitForWebRequest(GenerateRequest(httpRequest, bytes), request => - { - if (cancellationToken.IsCancellationRequested) - { - tcs.TrySetCanceled(); - return; - } - if (request.isDone) - { - uploadProgress.Report(new AVUploadProgressEventArgs { Progress = 1 }); - downloadProgress.Report(new AVDownloadProgressEventArgs { Progress = 1 }); - - var statusCode = GetResponseStatusCode(request); - // Returns HTTP error if that's the only info we have. - // if (!String.IsNullOrEmpty(www.error) && String.IsNullOrEmpty(www.text)) - if (!string.IsNullOrEmpty(request.error) && string.IsNullOrEmpty(request.downloadHandler.text)) - { - var errorString = string.Format("{{\"error\":\"{0}\"}}", request.error); - tcs.TrySetResult(new Tuple(statusCode, errorString)); - } - else - { - tcs.TrySetResult(new Tuple(statusCode, request.downloadHandler.text)); - } - } - else - { - // Update upload progress - var newUploadProgress = request.uploadProgress; - if (oldUploadProgress < newUploadProgress) - { - uploadProgress.Report(new AVUploadProgressEventArgs { Progress = newUploadProgress }); - } - oldUploadProgress = newUploadProgress; - - // Update download progress - var newDownloadProgress = request.downloadProgress; - if (oldDownloadProgress < newDownloadProgress) - { - downloadProgress.Report(new AVDownloadProgressEventArgs { Progress = newDownloadProgress }); - } - oldDownloadProgress = newDownloadProgress; - } - }); - }); - }); - - // Get off of the main thread for further processing. - return tcs.Task.ContinueWith(t => - { - var dispatchTcs = new TaskCompletionSource(); - // ThreadPool doesn't work well in IL2CPP environment, but Thread does! - if (isCompiledByIL2CPP) - { - var thread = new Thread(_ => - { - dispatchTcs.TrySetResult(null); - }); - thread.Start(); - } - else - { - ThreadPool.QueueUserWorkItem(_ => dispatchTcs.TrySetResult(null)); - } - return dispatchTcs.Task; - }).Unwrap() - .ContinueWith(_ => tcs.Task).Unwrap(); - } - - private static HttpStatusCode GetResponseStatusCode(UnityWebRequest request) - { - if (Enum.IsDefined(typeof(HttpStatusCode), (int)request.responseCode)) - { - return (HttpStatusCode)request.responseCode; - } - return (HttpStatusCode)400; - } - - private static UnityWebRequest GenerateRequest(HttpRequest request, byte[] bytes) - { - var webRequest = new UnityWebRequest(); - webRequest.method = request.Method; - webRequest.url = request.Uri.AbsoluteUri; - // Explicitly assume a JSON content. - webRequest.SetRequestHeader("Content-Type", "application/json"); - //webRequest.SetRequestHeader("User-Agent", "net-unity-" + AVVersionInfo.Version); - if (request.Headers != null) - { - foreach (var header in request.Headers) - { - webRequest.SetRequestHeader(header.Key as string, header.Value as string); - } - } - - if (bytes != null) - { - webRequest.uploadHandler = new UploadHandlerRaw(bytes); - } - webRequest.downloadHandler = new DownloadHandlerBuffer(); - webRequest.Send(); - return webRequest; - } - - private static void WaitForWebRequest(UnityWebRequest request, Action action) - { - Dispatcher.Instance.Post(() => - { - var isDone = request.isDone; - action(request); - if (!isDone) - { - WaitForWebRequest(request, action); - } - }); - } - } -} diff --git a/Storage/Source/Internal/IAVCorePlugins.cs b/Storage/Source/Internal/IAVCorePlugins.cs deleted file mode 100644 index 51060bd..0000000 --- a/Storage/Source/Internal/IAVCorePlugins.cs +++ /dev/null @@ -1,26 +0,0 @@ -using LeanCloud.Storage.Internal; -using System; - -namespace LeanCloud.Storage.Internal -{ - public interface IAVCorePlugins - { - void Reset(); - - IHttpClient HttpClient { get; } - IAppRouterController AppRouterController { get; } - IAVCommandRunner CommandRunner { get; } - IStorageController StorageController { get; } - - IAVCloudCodeController CloudCodeController { get; } - IAVConfigController ConfigController { get; } - IAVFileController FileController { get; } - IAVObjectController ObjectController { get; } - IAVQueryController QueryController { get; } - IAVSessionController SessionController { get; } - IAVUserController UserController { get; } - IObjectSubclassingController SubclassingController { get; } - IAVCurrentUserController CurrentUserController { get; } - IInstallationIdController InstallationIdController { get; } - } -} \ No newline at end of file diff --git a/Storage/Source/Internal/InstallationId/Controller/IInstallationIdController.cs b/Storage/Source/Internal/InstallationId/Controller/IInstallationIdController.cs deleted file mode 100644 index 1fb9af4..0000000 --- a/Storage/Source/Internal/InstallationId/Controller/IInstallationIdController.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal { - public interface IInstallationIdController { - /// - /// Sets current installationId and saves it to local storage. - /// - /// The installationId to be saved. - Task SetAsync(Guid? installationId); - - /// - /// Gets current installationId from local storage. Generates a none exists. - /// - /// Current installationId. - Task GetAsync(); - - /// - /// Clears current installationId from memory and local storage. - /// - Task ClearAsync(); - } -} diff --git a/Storage/Source/Internal/InstallationId/Controller/InstallationIdController.cs b/Storage/Source/Internal/InstallationId/Controller/InstallationIdController.cs deleted file mode 100644 index d28b015..0000000 --- a/Storage/Source/Internal/InstallationId/Controller/InstallationIdController.cs +++ /dev/null @@ -1,66 +0,0 @@ -using LeanCloud.Storage.Internal; -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal { - public class InstallationIdController : IInstallationIdController { - private const string InstallationIdKey = "InstallationId"; - private readonly object mutex = new object(); - private Guid? installationId; - - private readonly IStorageController storageController; - public InstallationIdController(IStorageController storageController) { - this.storageController = storageController; - } - - public Task SetAsync(Guid? installationId) { - lock (mutex) { - Task saveTask; - - if (installationId == null) { - saveTask = storageController - .LoadAsync() - .OnSuccess(storage => storage.Result.RemoveAsync(InstallationIdKey)) - .Unwrap(); - } else { - saveTask = storageController - .LoadAsync() - .OnSuccess(storage => storage.Result.AddAsync(InstallationIdKey, installationId.ToString())) - .Unwrap(); - } - this.installationId = installationId; - return saveTask; - } - } - - public Task GetAsync() { - lock (mutex) { - if (installationId != null) { - return Task.FromResult(installationId); - } - } - - return storageController - .LoadAsync() - .OnSuccess, Task>(s => { - object id; - s.Result.TryGetValue(InstallationIdKey, out id); - try { - lock (mutex) { - installationId = new Guid((string)id); - return Task.FromResult(installationId); - } - } catch (Exception) { - var newInstallationId = Guid.NewGuid(); - return SetAsync(newInstallationId).OnSuccess(_ => newInstallationId); - } - }) - .Unwrap(); - } - - public Task ClearAsync() { - return SetAsync(null); - } - } -} diff --git a/Storage/Source/Internal/Object/Controller/AVObjectController.cs b/Storage/Source/Internal/Object/Controller/AVObjectController.cs deleted file mode 100644 index 63e5a30..0000000 --- a/Storage/Source/Internal/Object/Controller/AVObjectController.cs +++ /dev/null @@ -1,248 +0,0 @@ -using System; -using System.Linq; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using LeanCloud.Utilities; -using LeanCloud.Storage.Internal; - -namespace LeanCloud.Storage.Internal -{ - public class AVObjectController : IAVObjectController - { - private readonly IAVCommandRunner commandRunner; - - public AVObjectController(IAVCommandRunner commandRunner) - { - this.commandRunner = commandRunner; - } - - public Task FetchAsync(IObjectState state, - string sessionToken, - CancellationToken cancellationToken) - { - var command = new AVCommand(string.Format("classes/{0}/{1}", - Uri.EscapeDataString(state.ClassName), - Uri.EscapeDataString(state.ObjectId)), - method: "GET", - sessionToken: sessionToken, - data: null); - - return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t => - { - return AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance); - }); - } - - public Task FetchAsync(IObjectState state, - IDictionary queryString, - string sessionToken, - CancellationToken cancellationToken) - { - var command = new AVCommand(string.Format("classes/{0}/{1}?{2}", - Uri.EscapeDataString(state.ClassName), - Uri.EscapeDataString(state.ObjectId), - AVClient.BuildQueryString(queryString)), - method: "GET", - sessionToken: sessionToken, - data: null); - - return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t => - { - return AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance); - }); - } - - public Task SaveAsync(IObjectState state, - IDictionary operations, - string sessionToken, - CancellationToken cancellationToken) - { - var objectJSON = AVObject.ToJSONObjectForSaving(operations); - - var command = new AVCommand((state.ObjectId == null ? - string.Format("classes/{0}", Uri.EscapeDataString(state.ClassName)) : - string.Format("classes/{0}/{1}", Uri.EscapeDataString(state.ClassName), state.ObjectId)), - method: (state.ObjectId == null ? "POST" : "PUT"), - sessionToken: sessionToken, - data: objectJSON); - - return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t => - { - var serverState = AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance); - serverState = serverState.MutatedClone(mutableClone => - { - mutableClone.IsNew = t.Result.Item1 == System.Net.HttpStatusCode.Created; - }); - return serverState; - }); - } - - public IList> SaveAllAsync(IList states, - IList> operationsList, - string sessionToken, - CancellationToken cancellationToken) - { - - var requests = states - .Zip(operationsList, (item, ops) => new AVCommand( - item.ObjectId == null - ? string.Format("classes/{0}", Uri.EscapeDataString(item.ClassName)) - : string.Format("classes/{0}/{1}", Uri.EscapeDataString(item.ClassName), Uri.EscapeDataString(item.ObjectId)), - method: item.ObjectId == null ? "POST" : "PUT", - data: AVObject.ToJSONObjectForSaving(ops))) - .ToList(); - - var batchTasks = ExecuteBatchRequests(requests, sessionToken, cancellationToken); - var stateTasks = new List>(); - foreach (var task in batchTasks) - { - stateTasks.Add(task.OnSuccess(t => - { - return AVObjectCoder.Instance.Decode(t.Result, AVDecoder.Instance); - })); - } - - return stateTasks; - } - - public Task DeleteAsync(IObjectState state, - string sessionToken, - CancellationToken cancellationToken) - { - var command = new AVCommand(string.Format("classes/{0}/{1}", - state.ClassName, state.ObjectId), - method: "DELETE", - sessionToken: sessionToken, - data: null); - - return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken); - } - - public IList DeleteAllAsync(IList states, - string sessionToken, - CancellationToken cancellationToken) - { - var requests = states - .Where(item => item.ObjectId != null) - .Select(item => new AVCommand( - string.Format("classes/{0}/{1}", Uri.EscapeDataString(item.ClassName), Uri.EscapeDataString(item.ObjectId)), - method: "DELETE", - data: null)) - .ToList(); - return ExecuteBatchRequests(requests, sessionToken, cancellationToken).Cast().ToList(); - } - - // TODO (hallucinogen): move this out to a class to be used by Analytics - private const int MaximumBatchSize = 50; - internal IList>> ExecuteBatchRequests(IList requests, - string sessionToken, - CancellationToken cancellationToken) - { - var tasks = new List>>(); - int batchSize = requests.Count; - - IEnumerable remaining = requests; - while (batchSize > MaximumBatchSize) - { - var process = remaining.Take(MaximumBatchSize).ToList(); - remaining = remaining.Skip(MaximumBatchSize); - - tasks.AddRange(ExecuteBatchRequest(process, sessionToken, cancellationToken)); - - batchSize = remaining.Count(); - } - tasks.AddRange(ExecuteBatchRequest(remaining.ToList(), sessionToken, cancellationToken)); - - return tasks; - } - - private IList>> ExecuteBatchRequest(IList requests, - string sessionToken, - CancellationToken cancellationToken) - { - var tasks = new List>>(); - int batchSize = requests.Count; - var tcss = new List>>(); - for (int i = 0; i < batchSize; ++i) - { - var tcs = new TaskCompletionSource>(); - tcss.Add(tcs); - tasks.Add(tcs.Task); - } - - var encodedRequests = requests.Select(r => - { - var results = new Dictionary { - { "method", r.Method }, - { "path", r.Uri.AbsolutePath }, - }; - - if (r.DataObject != null) - { - results["body"] = r.DataObject; - } - return results; - }).Cast().ToList(); - var command = new AVCommand("batch", - method: "POST", - sessionToken: sessionToken, - data: new Dictionary { { "requests", encodedRequests } }); - - commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).ContinueWith(t => - { - if (t.IsFaulted || t.IsCanceled) - { - foreach (var tcs in tcss) - { - if (t.IsFaulted) - { - tcs.TrySetException(t.Exception); - } - else if (t.IsCanceled) - { - tcs.TrySetCanceled(); - } - } - return; - } - - var resultsArray = Conversion.As>(t.Result.Item2["results"]); - int resultLength = resultsArray.Count; - if (resultLength != batchSize) - { - foreach (var tcs in tcss) - { - tcs.TrySetException(new InvalidOperationException( - "Batch command result count expected: " + batchSize + " but was: " + resultLength + ".")); - } - return; - } - - for (int i = 0; i < batchSize; ++i) - { - var result = resultsArray[i] as Dictionary; - var tcs = tcss[i]; - - if (result.ContainsKey("success")) - { - tcs.TrySetResult(result["success"] as IDictionary); - } - else if (result.ContainsKey("error")) - { - var error = result["error"] as IDictionary; - long errorCode = long.Parse(error["code"].ToString()); - tcs.TrySetException(new AVException((AVException.ErrorCode)errorCode, error["error"] as string)); - } - else - { - tcs.TrySetException(new InvalidOperationException( - "Invalid batch command response.")); - } - } - }); - - return tasks; - } - } -} diff --git a/Storage/Source/Internal/Object/Controller/IAVObjectController.cs b/Storage/Source/Internal/Object/Controller/IAVObjectController.cs deleted file mode 100644 index 9230eb5..0000000 --- a/Storage/Source/Internal/Object/Controller/IAVObjectController.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal -{ - public interface IAVObjectController - { - //Task FetchAsync(IObjectState state, - // string sessionToken, - // CancellationToken cancellationToken); - - Task FetchAsync(IObjectState state, - IDictionary queryString, - string sessionToken, - CancellationToken cancellationToken); - - Task SaveAsync(IObjectState state, - IDictionary operations, - string sessionToken, - CancellationToken cancellationToken); - - IList> SaveAllAsync(IList states, - IList> operationsList, - string sessionToken, - CancellationToken cancellationToken); - - Task DeleteAsync(IObjectState state, - string sessionToken, - CancellationToken cancellationToken); - - IList DeleteAllAsync(IList states, - string sessionToken, - CancellationToken cancellationToken); - } -} diff --git a/Storage/Source/Internal/Object/Controller/IAVObjectCurrentController.cs b/Storage/Source/Internal/Object/Controller/IAVObjectCurrentController.cs deleted file mode 100644 index e9b6f40..0000000 --- a/Storage/Source/Internal/Object/Controller/IAVObjectCurrentController.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal { - /// - /// IAVObjectCurrentController controls the single-instance - /// persistence used throughout the code-base. Sample usages are and - /// . - /// - /// Type of object being persisted. - public interface IAVObjectCurrentController where T : AVObject { - /// - /// Persists current . - /// - /// to be persisted. - /// The cancellation token. - Task SetAsync(T obj, CancellationToken cancellationToken); - - /// - /// Gets the persisted current . - /// - /// The cancellation token. - Task GetAsync(CancellationToken cancellationToken); - - /// - /// Returns a that resolves to true if current - /// exists. - /// - /// The cancellation token. - Task ExistsAsync(CancellationToken cancellationToken); - - /// - /// Returns true if the given is the persisted current - /// . - /// - /// The object to check. - /// True if obj is the current persisted . - bool IsCurrent(T obj); - - /// - /// Nullifies the current from memory. - /// - void ClearFromMemory(); - - /// - /// Clears current from disk. - /// - void ClearFromDisk(); - } -} diff --git a/Storage/Source/Internal/Object/State/IObjectState.cs b/Storage/Source/Internal/Object/State/IObjectState.cs deleted file mode 100644 index ab7b074..0000000 --- a/Storage/Source/Internal/Object/State/IObjectState.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace LeanCloud.Storage.Internal -{ - public interface IObjectState : IEnumerable> - { - bool IsNew { get; } - string ClassName { get; } - string ObjectId { get; } - DateTime? UpdatedAt { get; } - DateTime? CreatedAt { get; } - object this[string key] { get; } - - bool ContainsKey(string key); - - IObjectState MutatedClone(Action func); - } -} diff --git a/Storage/Source/Internal/Object/State/MutableObjectState.cs b/Storage/Source/Internal/Object/State/MutableObjectState.cs deleted file mode 100644 index ff9be3a..0000000 --- a/Storage/Source/Internal/Object/State/MutableObjectState.cs +++ /dev/null @@ -1,114 +0,0 @@ -using System; -using System.Linq; -using System.Collections.Generic; - -namespace LeanCloud.Storage.Internal -{ - public class MutableObjectState : IObjectState - { - public bool IsNew { get; set; } - public string ClassName { get; set; } - public string ObjectId { get; set; } - public DateTime? UpdatedAt { get; set; } - public DateTime? CreatedAt { get; set; } - - // Initialize serverData to avoid further null checking. - private IDictionary serverData = new Dictionary(); - public IDictionary ServerData - { - get - { - return serverData; - } - - set - { - serverData = value; - } - } - - public object this[string key] - { - get - { - return ServerData[key]; - } - } - - public bool ContainsKey(string key) - { - return ServerData.ContainsKey(key); - } - - public void Apply(IDictionary operationSet) - { - // Apply operationSet - foreach (var pair in operationSet) - { - object oldValue; - ServerData.TryGetValue(pair.Key, out oldValue); - var newValue = pair.Value.Apply(oldValue, pair.Key); - if (newValue != AVDeleteOperation.DeleteToken) - { - ServerData[pair.Key] = newValue; - } - else - { - ServerData.Remove(pair.Key); - } - } - } - - public void Apply(IObjectState other) - { - IsNew = other.IsNew; - if (other.ObjectId != null) - { - ObjectId = other.ObjectId; - } - if (other.UpdatedAt != null) - { - UpdatedAt = other.UpdatedAt; - } - if (other.CreatedAt != null) - { - CreatedAt = other.CreatedAt; - } - - foreach (var pair in other) - { - ServerData[pair.Key] = pair.Value; - } - } - - public IObjectState MutatedClone(Action func) - { - var clone = MutableClone(); - func(clone); - return clone; - } - - protected virtual MutableObjectState MutableClone() - { - return new MutableObjectState - { - IsNew = IsNew, - ClassName = ClassName, - ObjectId = ObjectId, - CreatedAt = CreatedAt, - UpdatedAt = UpdatedAt, - ServerData = this.ToDictionary(t => t.Key, t => t.Value) - }; - } - - IEnumerator> IEnumerable>.GetEnumerator() - { - return ServerData.GetEnumerator(); - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return ((IEnumerable>)this).GetEnumerator(); - } - } -} diff --git a/Storage/Source/Internal/Object/Subclassing/IObjectSubclassingController.cs b/Storage/Source/Internal/Object/Subclassing/IObjectSubclassingController.cs deleted file mode 100644 index 38322fc..0000000 --- a/Storage/Source/Internal/Object/Subclassing/IObjectSubclassingController.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal -{ - public interface IObjectSubclassingController - { - String GetClassName(Type type); - Type GetType(String className); - - bool IsTypeValid(String className, Type type); - - void RegisterSubclass(Type t); - void UnregisterSubclass(Type t); - - void AddRegisterHook(Type t, Action action); - - AVObject Instantiate(String className); - IDictionary GetPropertyMappings(String className); - } -} diff --git a/Storage/Source/Internal/Object/Subclassing/ObjectSubclassInfo.cs b/Storage/Source/Internal/Object/Subclassing/ObjectSubclassInfo.cs deleted file mode 100644 index 080fe6c..0000000 --- a/Storage/Source/Internal/Object/Subclassing/ObjectSubclassInfo.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Reflection; -using LeanCloud.Storage.Internal; - -namespace LeanCloud.Storage.Internal -{ - internal class ObjectSubclassInfo - { - public ObjectSubclassInfo(Type type, ConstructorInfo constructor) - { - TypeInfo = type.GetTypeInfo(); - ClassName = GetClassName(TypeInfo); - Constructor = constructor; - PropertyMappings = ReflectionHelpers.GetProperties(type) - .Select(prop => Tuple.Create(prop, prop.GetCustomAttribute(true))) - .Where(t => t.Item2 != null) - .Select(t => Tuple.Create(t.Item1, t.Item2.FieldName)) - .ToDictionary(t => t.Item1.Name, t => t.Item2); - } - - public TypeInfo TypeInfo { get; private set; } - public String ClassName { get; private set; } - public IDictionary PropertyMappings { get; private set; } - private ConstructorInfo Constructor { get; set; } - - public AVObject Instantiate() - { - return (AVObject)Constructor.Invoke(null); - } - - internal static String GetClassName(TypeInfo type) - { - var attribute = type.GetCustomAttribute(); - return attribute != null ? attribute.ClassName : null; - } - } -} diff --git a/Storage/Source/Internal/Object/Subclassing/ObjectSubclassingController.cs b/Storage/Source/Internal/Object/Subclassing/ObjectSubclassingController.cs deleted file mode 100644 index b1f913e..0000000 --- a/Storage/Source/Internal/Object/Subclassing/ObjectSubclassingController.cs +++ /dev/null @@ -1,171 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Threading; -using LeanCloud.Storage.Internal; - -namespace LeanCloud.Storage.Internal -{ - internal class ObjectSubclassingController : IObjectSubclassingController - { - // Class names starting with _ are documented to be reserved. Use this one - // here to allow us to 'inherit' certain properties. - private static readonly string avObjectClassName = "_AVObject"; - - private readonly ReaderWriterLockSlim mutex; - private readonly IDictionary registeredSubclasses; - private Dictionary registerActions; - - public ObjectSubclassingController() - { - mutex = new ReaderWriterLockSlim(); - registeredSubclasses = new Dictionary(); - registerActions = new Dictionary(); - - // Register the AVObject subclass, so we get access to the ACL, - // objectId, and other AVFieldName properties. - RegisterSubclass(typeof(AVObject)); - } - - public String GetClassName(Type type) - { - return type == typeof(AVObject) - ? avObjectClassName - : ObjectSubclassInfo.GetClassName(type.GetTypeInfo()); - } - - public Type GetType(String className) - { - ObjectSubclassInfo info = null; - mutex.EnterReadLock(); - registeredSubclasses.TryGetValue(className, out info); - mutex.ExitReadLock(); - - return info != null - ? info.TypeInfo.AsType() - : null; - } - - public bool IsTypeValid(String className, Type type) - { - ObjectSubclassInfo subclassInfo = null; - - mutex.EnterReadLock(); - registeredSubclasses.TryGetValue(className, out subclassInfo); - mutex.ExitReadLock(); - - return subclassInfo == null - ? type == typeof(AVObject) - : subclassInfo.TypeInfo == type.GetTypeInfo(); - } - - public void RegisterSubclass(Type type) - { - TypeInfo typeInfo = type.GetTypeInfo(); - if (!typeof(AVObject).GetTypeInfo().IsAssignableFrom(typeInfo)) - { - throw new ArgumentException("Cannot register a type that is not a subclass of AVObject"); - } - - String className = GetClassName(type); - - try - { - // Perform this as a single independent transaction, so we can never get into an - // intermediate state where we *theoretically* register the wrong class due to a - // TOCTTOU bug. - mutex.EnterWriteLock(); - - ObjectSubclassInfo previousInfo = null; - if (registeredSubclasses.TryGetValue(className, out previousInfo)) - { - if (typeInfo.IsAssignableFrom(previousInfo.TypeInfo)) - { - // Previous subclass is more specific or equal to the current type, do nothing. - return; - } - else if (previousInfo.TypeInfo.IsAssignableFrom(typeInfo)) - { - // Previous subclass is parent of new child, fallthrough and actually register - // this class. - /* Do nothing */ - } - else - { - throw new ArgumentException( - "Tried to register both " + previousInfo.TypeInfo.FullName + " and " + typeInfo.FullName + - " as the AVObject subclass of " + className + ". Cannot determine the right class " + - "to use because neither inherits from the other." - ); - } - } - - ConstructorInfo constructor = type.FindConstructor(); - if (constructor == null) - { - throw new ArgumentException("Cannot register a type that does not implement the default constructor!"); - } - - registeredSubclasses[className] = new ObjectSubclassInfo(type, constructor); - } - finally - { - mutex.ExitWriteLock(); - } - - Action toPerform; - - mutex.EnterReadLock(); - registerActions.TryGetValue(className, out toPerform); - mutex.ExitReadLock(); - - if (toPerform != null) - { - toPerform(); - } - } - - public void UnregisterSubclass(Type type) - { - mutex.EnterWriteLock(); - registeredSubclasses.Remove(GetClassName(type)); - mutex.ExitWriteLock(); - } - - public void AddRegisterHook(Type t, Action action) - { - mutex.EnterWriteLock(); - registerActions.Add(GetClassName(t), action); - mutex.ExitWriteLock(); - } - - public AVObject Instantiate(String className) - { - ObjectSubclassInfo info = null; - - mutex.EnterReadLock(); - registeredSubclasses.TryGetValue(className, out info); - mutex.ExitReadLock(); - - return info != null - ? info.Instantiate() - : new AVObject(className); - } - - public IDictionary GetPropertyMappings(String className) - { - ObjectSubclassInfo info = null; - mutex.EnterReadLock(); - registeredSubclasses.TryGetValue(className, out info); - if (info == null) - { - registeredSubclasses.TryGetValue(avObjectClassName, out info); - } - mutex.ExitReadLock(); - - return info.PropertyMappings; - } - - } -} diff --git a/Storage/Source/Internal/Operation/AVAddOperation.cs b/Storage/Source/Internal/Operation/AVAddOperation.cs deleted file mode 100644 index ed0a83e..0000000 --- a/Storage/Source/Internal/Operation/AVAddOperation.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using LeanCloud.Utilities; - -namespace LeanCloud.Storage.Internal { - public class AVAddOperation : IAVFieldOperation { - private ReadOnlyCollection objects; - public AVAddOperation(IEnumerable objects) { - this.objects = new ReadOnlyCollection(objects.ToList()); - } - - public object Encode() { - return new Dictionary { - {"__op", "Add"}, - {"objects", PointerOrLocalIdEncoder.Instance.Encode(objects)} - }; - } - - public IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous) { - if (previous == null) { - return this; - } - if (previous is AVDeleteOperation) { - return new AVSetOperation(objects.ToList()); - } - if (previous is AVSetOperation) { - var setOp = (AVSetOperation)previous; - var oldList = Conversion.To>(setOp.Value); - return new AVSetOperation(oldList.Concat(objects).ToList()); - } - if (previous is AVAddOperation) { - return new AVAddOperation(((AVAddOperation)previous).Objects.Concat(objects)); - } - throw new InvalidOperationException("Operation is invalid after previous operation."); - } - - public object Apply(object oldValue, string key) { - if (oldValue == null) { - return objects.ToList(); - } - var oldList = Conversion.To>(oldValue); - return oldList.Concat(objects).ToList(); - } - - public IEnumerable Objects { - get { - return objects; - } - } - } -} diff --git a/Storage/Source/Internal/Operation/AVAddUniqueOperation.cs b/Storage/Source/Internal/Operation/AVAddUniqueOperation.cs deleted file mode 100644 index 8e0a957..0000000 --- a/Storage/Source/Internal/Operation/AVAddUniqueOperation.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using LeanCloud.Utilities; - -namespace LeanCloud.Storage.Internal { - public class AVAddUniqueOperation : IAVFieldOperation { - private ReadOnlyCollection objects; - public AVAddUniqueOperation(IEnumerable objects) { - this.objects = new ReadOnlyCollection(objects.Distinct().ToList()); - } - - public object Encode() { - return new Dictionary { - {"__op", "AddUnique"}, - {"objects", PointerOrLocalIdEncoder.Instance.Encode(objects)} - }; - } - - public IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous) { - if (previous == null) { - return this; - } - if (previous is AVDeleteOperation) { - return new AVSetOperation(objects.ToList()); - } - if (previous is AVSetOperation) { - var setOp = (AVSetOperation)previous; - var oldList = Conversion.To>(setOp.Value); - var result = this.Apply(oldList, null); - return new AVSetOperation(result); - } - if (previous is AVAddUniqueOperation) { - var oldList = ((AVAddUniqueOperation)previous).Objects; - return new AVAddUniqueOperation((IList)this.Apply(oldList, null)); - } - throw new InvalidOperationException("Operation is invalid after previous operation."); - } - - public object Apply(object oldValue, string key) { - if (oldValue == null) { - return objects.ToList(); - } - var newList = Conversion.To>(oldValue).ToList(); - var comparer = AVFieldOperations.AVObjectComparer; - foreach (var objToAdd in objects) { - if (objToAdd is AVObject) { - var matchedObj = newList.FirstOrDefault(listObj => comparer.Equals(objToAdd, listObj)); - if (matchedObj == null) { - newList.Add(objToAdd); - } else { - var index = newList.IndexOf(matchedObj); - newList[index] = objToAdd; - } - } else if (!newList.Contains(objToAdd, comparer)) { - newList.Add(objToAdd); - } - } - return newList; - } - - public IEnumerable Objects { - get { - return objects; - } - } - } -} diff --git a/Storage/Source/Internal/Operation/AVDeleteOperation.cs b/Storage/Source/Internal/Operation/AVDeleteOperation.cs deleted file mode 100644 index 7b77b94..0000000 --- a/Storage/Source/Internal/Operation/AVDeleteOperation.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Collections.Generic; - -namespace LeanCloud.Storage.Internal -{ - /// - /// An operation where a field is deleted from the object. - /// - public class AVDeleteOperation : IAVFieldOperation - { - internal static readonly object DeleteToken = new object(); - private static AVDeleteOperation _Instance = new AVDeleteOperation(); - public static AVDeleteOperation Instance - { - get - { - return _Instance; - } - } - - private AVDeleteOperation() { } - public object Encode() - { - return new Dictionary { - {"__op", "Delete"} - }; - } - - public IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous) - { - return this; - } - - public object Apply(object oldValue, string key) - { - return DeleteToken; - } - } -} diff --git a/Storage/Source/Internal/Operation/AVFieldOperations.cs b/Storage/Source/Internal/Operation/AVFieldOperations.cs deleted file mode 100644 index 9c07ffa..0000000 --- a/Storage/Source/Internal/Operation/AVFieldOperations.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace LeanCloud.Storage.Internal { - public class AVObjectIdComparer : IEqualityComparer { - bool IEqualityComparer.Equals(object p1, object p2) { - var avObj1 = p1 as AVObject; - var avObj2 = p2 as AVObject; - if (avObj1 != null && avObj2 != null) { - return object.Equals(avObj1.ObjectId, avObj2.ObjectId); - } - return object.Equals(p1, p2); - } - - public int GetHashCode(object p) { - var avObject = p as AVObject; - if (avObject != null) { - return avObject.ObjectId.GetHashCode(); - } - return p.GetHashCode(); - } - } - - static class AVFieldOperations { - private static AVObjectIdComparer comparer; - - public static IAVFieldOperation Decode(IDictionary json) { - throw new NotImplementedException(); - } - - public static IEqualityComparer AVObjectComparer { - get { - if (comparer == null) { - comparer = new AVObjectIdComparer(); - } - return comparer; - } - } - } -} diff --git a/Storage/Source/Internal/Operation/AVIncrementOperation.cs b/Storage/Source/Internal/Operation/AVIncrementOperation.cs deleted file mode 100644 index 1606e5f..0000000 --- a/Storage/Source/Internal/Operation/AVIncrementOperation.cs +++ /dev/null @@ -1,166 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace LeanCloud.Storage.Internal -{ - public class AVIncrementOperation : IAVFieldOperation - { - private static readonly IDictionary, Func> adders; - - static AVIncrementOperation() - { - // Defines adders for all of the implicit conversions: http://msdn.microsoft.com/en-US/library/y5b434w4(v=vs.80).aspx - adders = new Dictionary, Func> { - {new Tuple(typeof(sbyte), typeof(sbyte)), (left, right) => (sbyte)left + (sbyte)right}, - {new Tuple(typeof(sbyte), typeof(short)), (left, right) => (sbyte)left + (short)right}, - {new Tuple(typeof(sbyte), typeof(int)), (left, right) => (sbyte)left + (int)right}, - {new Tuple(typeof(sbyte), typeof(long)), (left, right) => (sbyte)left + (long)right}, - {new Tuple(typeof(sbyte), typeof(float)), (left, right) => (sbyte)left + (float)right}, - {new Tuple(typeof(sbyte), typeof(double)), (left, right) => (sbyte)left + (double)right}, - {new Tuple(typeof(sbyte), typeof(decimal)), (left, right) => (sbyte)left + (decimal)right}, - {new Tuple(typeof(byte), typeof(byte)), (left, right) => (byte)left + (byte)right}, - {new Tuple(typeof(byte), typeof(short)), (left, right) => (byte)left + (short)right}, - {new Tuple(typeof(byte), typeof(ushort)), (left, right) => (byte)left + (ushort)right}, - {new Tuple(typeof(byte), typeof(int)), (left, right) => (byte)left + (int)right}, - {new Tuple(typeof(byte), typeof(uint)), (left, right) => (byte)left + (uint)right}, - {new Tuple(typeof(byte), typeof(long)), (left, right) => (byte)left + (long)right}, - {new Tuple(typeof(byte), typeof(ulong)), (left, right) => (byte)left + (ulong)right}, - {new Tuple(typeof(byte), typeof(float)), (left, right) => (byte)left + (float)right}, - {new Tuple(typeof(byte), typeof(double)), (left, right) => (byte)left + (double)right}, - {new Tuple(typeof(byte), typeof(decimal)), (left, right) => (byte)left + (decimal)right}, - {new Tuple(typeof(short), typeof(short)), (left, right) => (short)left + (short)right}, - {new Tuple(typeof(short), typeof(int)), (left, right) => (short)left + (int)right}, - {new Tuple(typeof(short), typeof(long)), (left, right) => (short)left + (long)right}, - {new Tuple(typeof(short), typeof(float)), (left, right) => (short)left + (float)right}, - {new Tuple(typeof(short), typeof(double)), (left, right) => (short)left + (double)right}, - {new Tuple(typeof(short), typeof(decimal)), (left, right) => (short)left + (decimal)right}, - {new Tuple(typeof(ushort), typeof(ushort)), (left, right) => (ushort)left + (ushort)right}, - {new Tuple(typeof(ushort), typeof(int)), (left, right) => (ushort)left + (int)right}, - {new Tuple(typeof(ushort), typeof(uint)), (left, right) => (ushort)left + (uint)right}, - {new Tuple(typeof(ushort), typeof(long)), (left, right) => (ushort)left + (long)right}, - {new Tuple(typeof(ushort), typeof(ulong)), (left, right) => (ushort)left + (ulong)right}, - {new Tuple(typeof(ushort), typeof(float)), (left, right) => (ushort)left + (float)right}, - {new Tuple(typeof(ushort), typeof(double)), (left, right) => (ushort)left + (double)right}, - {new Tuple(typeof(ushort), typeof(decimal)), (left, right) => (ushort)left + (decimal)right}, - {new Tuple(typeof(int), typeof(int)), (left, right) => (int)left + (int)right}, - {new Tuple(typeof(int), typeof(long)), (left, right) => (int)left + (long)right}, - {new Tuple(typeof(int), typeof(float)), (left, right) => (int)left + (float)right}, - {new Tuple(typeof(int), typeof(double)), (left, right) => (int)left + (double)right}, - {new Tuple(typeof(int), typeof(decimal)), (left, right) => (int)left + (decimal)right}, - {new Tuple(typeof(uint), typeof(uint)), (left, right) => (uint)left + (uint)right}, - {new Tuple(typeof(uint), typeof(long)), (left, right) => (uint)left + (long)right}, - {new Tuple(typeof(uint), typeof(ulong)), (left, right) => (uint)left + (ulong)right}, - {new Tuple(typeof(uint), typeof(float)), (left, right) => (uint)left + (float)right}, - {new Tuple(typeof(uint), typeof(double)), (left, right) => (uint)left + (double)right}, - {new Tuple(typeof(uint), typeof(decimal)), (left, right) => (uint)left + (decimal)right}, - {new Tuple(typeof(long), typeof(long)), (left, right) => (long)left + (long)right}, - {new Tuple(typeof(long), typeof(float)), (left, right) => (long)left + (float)right}, - {new Tuple(typeof(long), typeof(double)), (left, right) => (long)left + (double)right}, - {new Tuple(typeof(long), typeof(decimal)), (left, right) => (long)left + (decimal)right}, - {new Tuple(typeof(char), typeof(char)), (left, right) => (char)left + (char)right}, - {new Tuple(typeof(char), typeof(ushort)), (left, right) => (char)left + (ushort)right}, - {new Tuple(typeof(char), typeof(int)), (left, right) => (char)left + (int)right}, - {new Tuple(typeof(char), typeof(uint)), (left, right) => (char)left + (uint)right}, - {new Tuple(typeof(char), typeof(long)), (left, right) => (char)left + (long)right}, - {new Tuple(typeof(char), typeof(ulong)), (left, right) => (char)left + (ulong)right}, - {new Tuple(typeof(char), typeof(float)), (left, right) => (char)left + (float)right}, - {new Tuple(typeof(char), typeof(double)), (left, right) => (char)left + (double)right}, - {new Tuple(typeof(char), typeof(decimal)), (left, right) => (char)left + (decimal)right}, - {new Tuple(typeof(float), typeof(float)), (left, right) => (float)left + (float)right}, - {new Tuple(typeof(float), typeof(double)), (left, right) => (float)left + (double)right}, - {new Tuple(typeof(ulong), typeof(ulong)), (left, right) => (ulong)left + (ulong)right}, - {new Tuple(typeof(ulong), typeof(float)), (left, right) => (ulong)left + (float)right}, - {new Tuple(typeof(ulong), typeof(double)), (left, right) => (ulong)left + (double)right}, - {new Tuple(typeof(ulong), typeof(decimal)), (left, right) => (ulong)left + (decimal)right}, - {new Tuple(typeof(double), typeof(double)), (left, right) => (double)left + (double)right}, - {new Tuple(typeof(decimal), typeof(decimal)), (left, right) => (decimal)left + (decimal)right} - }; - // Generate the adders in the other direction - foreach (var pair in adders.Keys.ToList()) - { - if (pair.Item1.Equals(pair.Item2)) - { - continue; - } - var reversePair = new Tuple(pair.Item2, pair.Item1); - var func = adders[pair]; - adders[reversePair] = (left, right) => func(right, left); - } - } - - private object amount; - - public AVIncrementOperation(object amount) - { - this.amount = amount; - } - - public object Encode() - { - return new Dictionary - { - {"__op", "Increment"}, - {"amount", amount} - }; - } - - private static object Add(object obj1, object obj2) - { - Func adder; - if (adders.TryGetValue(new Tuple(obj1.GetType(), obj2.GetType()), out adder)) - { - return adder(obj1, obj2); - } - throw new InvalidCastException("Cannot add " + obj1.GetType() + " to " + obj2.GetType()); - } - - public IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous) - { - if (previous == null) - { - return this; - } - if (previous is AVDeleteOperation) - { - return new AVSetOperation(amount); - } - if (previous is AVSetOperation) - { - var otherAmount = ((AVSetOperation)previous).Value; - if (otherAmount is string) - { - throw new InvalidOperationException("Cannot increment a non-number type."); - } - var myAmount = amount; - return new AVSetOperation(Add(otherAmount, myAmount)); - } - if (previous is AVIncrementOperation) - { - object otherAmount = ((AVIncrementOperation)previous).Amount; - object myAmount = amount; - return new AVIncrementOperation(Add(otherAmount, myAmount)); - } - throw new InvalidOperationException("Operation is invalid after previous operation."); - } - - public object Apply(object oldValue, string key) - { - if (oldValue is string) - { - throw new InvalidOperationException("Cannot increment a non-number type."); - } - object otherAmount = oldValue ?? 0; - object myAmount = amount; - return Add(otherAmount, myAmount); - } - - public object Amount - { - get - { - return amount; - } - } - } -} diff --git a/Storage/Source/Internal/Operation/AVRelationOperation.cs b/Storage/Source/Internal/Operation/AVRelationOperation.cs deleted file mode 100644 index 512b5f5..0000000 --- a/Storage/Source/Internal/Operation/AVRelationOperation.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal { - public class AVRelationOperation : IAVFieldOperation { - private readonly IList adds; - private readonly IList removes; - private readonly string targetClassName; - - private AVRelationOperation(IEnumerable adds, - IEnumerable removes, - string targetClassName) { - this.targetClassName = targetClassName; - this.adds = new ReadOnlyCollection(adds.ToList()); - this.removes = new ReadOnlyCollection(removes.ToList()); - } - - public AVRelationOperation(IEnumerable adds, - IEnumerable removes) { - adds = adds ?? new AVObject[0]; - removes = removes ?? new AVObject[0]; - this.targetClassName = adds.Concat(removes).Select(o => o.ClassName).FirstOrDefault(); - this.adds = new ReadOnlyCollection(IdsFromObjects(adds).ToList()); - this.removes = new ReadOnlyCollection(IdsFromObjects(removes).ToList()); - } - - public object Encode() { - var adds = this.adds - .Select(id => PointerOrLocalIdEncoder.Instance.Encode( - AVObject.CreateWithoutData(targetClassName, id))) - .ToList(); - var removes = this.removes - .Select(id => PointerOrLocalIdEncoder.Instance.Encode( - AVObject.CreateWithoutData(targetClassName, id))) - .ToList(); - var addDict = adds.Count == 0 ? null : new Dictionary { - {"__op", "AddRelation"}, - {"objects", adds} - }; - var removeDict = removes.Count == 0 ? null : new Dictionary { - {"__op", "RemoveRelation"}, - {"objects", removes} - }; - - if (addDict != null && removeDict != null) { - return new Dictionary { - {"__op", "Batch"}, - {"ops", new[] {addDict, removeDict}} - }; - } - return addDict ?? removeDict; - } - - public IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous) { - if (previous == null) { - return this; - } - if (previous is AVDeleteOperation) { - throw new InvalidOperationException("You can't modify a relation after deleting it."); - } - var other = previous as AVRelationOperation; - if (other != null) { - if (other.TargetClassName != TargetClassName) { - throw new InvalidOperationException( - string.Format("Related object must be of class {0}, but {1} was passed in.", - other.TargetClassName, - TargetClassName)); - } - var newAdd = adds.Union(other.adds.Except(removes)).ToList(); - var newRemove = removes.Union(other.removes.Except(adds)).ToList(); - return new AVRelationOperation(newAdd, newRemove, TargetClassName); - } - throw new InvalidOperationException("Operation is invalid after previous operation."); - } - - public object Apply(object oldValue, string key) { - if (adds.Count == 0 && removes.Count == 0) { - return null; - } - if (oldValue == null) { - return AVRelationBase.CreateRelation(null, key, targetClassName); - } - if (oldValue is AVRelationBase) { - var oldRelation = (AVRelationBase)oldValue; - var oldClassName = oldRelation.TargetClassName; - if (oldClassName != null && oldClassName != targetClassName) { - throw new InvalidOperationException("Related object must be a " + oldClassName - + ", but a " + targetClassName + " was passed in."); - } - oldRelation.TargetClassName = targetClassName; - return oldRelation; - } - throw new InvalidOperationException("Operation is invalid after previous operation."); - } - - public string TargetClassName { get { return targetClassName; } } - - private IEnumerable IdsFromObjects(IEnumerable objects) { - foreach (var obj in objects) { - if (obj.ObjectId == null) { - throw new ArgumentException( - "You can't add an unsaved AVObject to a relation."); - } - if (obj.ClassName != targetClassName) { - throw new ArgumentException(string.Format( - "Tried to create a AVRelation with 2 different types: {0} and {1}", - targetClassName, - obj.ClassName)); - } - } - return objects.Select(o => o.ObjectId).Distinct(); - } - } -} diff --git a/Storage/Source/Internal/Operation/AVRemoveOperation.cs b/Storage/Source/Internal/Operation/AVRemoveOperation.cs deleted file mode 100644 index c9d14a4..0000000 --- a/Storage/Source/Internal/Operation/AVRemoveOperation.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; - -using LeanCloud.Utilities; - -namespace LeanCloud.Storage.Internal -{ - public class AVRemoveOperation : IAVFieldOperation - { - private ReadOnlyCollection objects; - public AVRemoveOperation(IEnumerable objects) - { - this.objects = new ReadOnlyCollection(objects.Distinct().ToList()); - } - - public object Encode() - { - return new Dictionary { - {"__op", "Remove"}, - {"objects", PointerOrLocalIdEncoder.Instance.Encode(objects)} - }; - } - - public IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous) - { - if (previous == null) - { - return this; - } - if (previous is AVDeleteOperation) - { - return previous; - } - if (previous is AVSetOperation) - { - var setOp = (AVSetOperation)previous; - var oldList = Conversion.As>(setOp.Value); - return new AVSetOperation(this.Apply(oldList, null)); - } - if (previous is AVRemoveOperation) - { - var oldOp = (AVRemoveOperation)previous; - return new AVRemoveOperation(oldOp.Objects.Concat(objects)); - } - throw new InvalidOperationException("Operation is invalid after previous operation."); - } - - public object Apply(object oldValue, string key) - { - if (oldValue == null) - { - return new List(); - } - var oldList = Conversion.As>(oldValue); - return oldList.Except(objects, AVFieldOperations.AVObjectComparer).ToList(); - } - - public IEnumerable Objects - { - get - { - return objects; - } - } - } -} diff --git a/Storage/Source/Internal/Operation/AVSetOperation.cs b/Storage/Source/Internal/Operation/AVSetOperation.cs deleted file mode 100644 index 1c3a412..0000000 --- a/Storage/Source/Internal/Operation/AVSetOperation.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace LeanCloud.Storage.Internal { - public class AVSetOperation : IAVFieldOperation { - public AVSetOperation(object value) { - Value = value; - } - - public object Encode() { - return PointerOrLocalIdEncoder.Instance.Encode(Value); - } - - public IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous) { - return this; - } - - public object Apply(object oldValue, string key) { - return Value; - } - - public object Value { get; private set; } - } -} diff --git a/Storage/Source/Internal/Operation/IAVFieldOperation.cs b/Storage/Source/Internal/Operation/IAVFieldOperation.cs deleted file mode 100644 index 70b2930..0000000 --- a/Storage/Source/Internal/Operation/IAVFieldOperation.cs +++ /dev/null @@ -1,42 +0,0 @@ -namespace LeanCloud.Storage.Internal { - /// - /// A AVFieldOperation represents a modification to a value in a AVObject. - /// For example, setting, deleting, or incrementing a value are all different kinds of - /// AVFieldOperations. AVFieldOperations themselves can be considered to be - /// immutable. - /// - public interface IAVFieldOperation { - /// - /// Converts the AVFieldOperation to a data structure that can be converted to JSON and sent to - /// LeanCloud as part of a save operation. - /// - /// An object to be JSONified. - object Encode(); - - /// - /// Returns a field operation that is composed of a previous operation followed by - /// this operation. This will not mutate either operation. However, it may return - /// this if the current operation is not affected by previous changes. - /// For example: - /// {increment by 2}.MergeWithPrevious({set to 5}) -> {set to 7} - /// {set to 5}.MergeWithPrevious({increment by 2}) -> {set to 5} - /// {add "foo"}.MergeWithPrevious({delete}) -> {set to ["foo"]} - /// {delete}.MergeWithPrevious({add "foo"}) -> {delete} /// - /// The most recent operation on the field, or null if none. - /// A new AVFieldOperation or this. - IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous); - - /// - /// Returns a new estimated value based on a previous value and this operation. This - /// value is not intended to be sent to LeanCloud, but it is used locally on the client to - /// inspect the most likely current value for a field. - /// - /// The key and object are used solely for AVRelation to be able to construct objects - /// that refer back to their parents. - /// - /// The previous value for the field. - /// The key that this value is for. - /// The new value for the field. - object Apply(object oldValue, string key); - } -} diff --git a/Storage/Source/Internal/Query/Controller/AVQueryController.cs b/Storage/Source/Internal/Query/Controller/AVQueryController.cs deleted file mode 100644 index 42c1869..0000000 --- a/Storage/Source/Internal/Query/Controller/AVQueryController.cs +++ /dev/null @@ -1,110 +0,0 @@ -using System; -using System.Linq; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Threading; -using System.Threading.Tasks; -using LeanCloud.Storage.Internal; - -namespace LeanCloud.Storage.Internal -{ - internal class AVQueryController : IAVQueryController - { - private readonly IAVCommandRunner commandRunner; - - public AVQueryController(IAVCommandRunner commandRunner) - { - this.commandRunner = commandRunner; - } - - public Task> FindAsync(AVQuery query, - AVUser user, - CancellationToken cancellationToken) where T : AVObject - { - string sessionToken = user != null ? user.SessionToken : null; - - return FindAsync(query.RelativeUri, query.BuildParameters(), sessionToken, cancellationToken).OnSuccess(t => - { - var items = t.Result["results"] as IList; - - return (from item in items - select AVObjectCoder.Instance.Decode(item as IDictionary, AVDecoder.Instance)); - }); - } - - public Task CountAsync(AVQuery query, - AVUser user, - CancellationToken cancellationToken) where T : AVObject - { - string sessionToken = user != null ? user.SessionToken : null; - var parameters = query.BuildParameters(); - parameters["limit"] = 0; - parameters["count"] = 1; - - return FindAsync(query.RelativeUri, parameters, sessionToken, cancellationToken).OnSuccess(t => - { - return Convert.ToInt32(t.Result["count"]); - }); - } - - public Task FirstAsync(AVQuery query, - AVUser user, - CancellationToken cancellationToken) where T : AVObject - { - string sessionToken = user != null ? user.SessionToken : null; - var parameters = query.BuildParameters(); - parameters["limit"] = 1; - - return FindAsync(query.RelativeUri, parameters, sessionToken, cancellationToken).OnSuccess(t => - { - var items = t.Result["results"] as IList; - var item = items.FirstOrDefault() as IDictionary; - - // Not found. Return empty state. - if (item == null) - { - return (IObjectState)null; - } - - return AVObjectCoder.Instance.Decode(item, AVDecoder.Instance); - }); - } - - private Task> FindAsync(string relativeUri, - IDictionary parameters, - string sessionToken, - CancellationToken cancellationToken) - { - - var command = new AVCommand(string.Format("{0}?{1}", - relativeUri, - AVClient.BuildQueryString(parameters)), - method: "GET", - sessionToken: sessionToken, - data: null); - - return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t => - { - return t.Result.Item2; - }); - } - - //private Task> FindAsync(string className, - // IDictionary parameters, - // string sessionToken, - // CancellationToken cancellationToken) - //{ - // var command = new AVCommand(string.Format("classes/{0}?{1}", - // Uri.EscapeDataString(className), - // AVClient.BuildQueryString(parameters)), - // method: "GET", - // sessionToken: sessionToken, - // data: null); - - // return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t => - // { - // return t.Result.Item2; - // }); - //} - } -} diff --git a/Storage/Source/Internal/Query/Controller/IAVQueryController.cs b/Storage/Source/Internal/Query/Controller/IAVQueryController.cs deleted file mode 100644 index f5d1494..0000000 --- a/Storage/Source/Internal/Query/Controller/IAVQueryController.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Linq; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal { - public interface IAVQueryController { - Task> FindAsync(AVQuery query, - AVUser user, - CancellationToken cancellationToken) where T : AVObject; - - Task CountAsync(AVQuery query, - AVUser user, - CancellationToken cancellationToken) where T : AVObject; - - Task FirstAsync(AVQuery query, - AVUser user, - CancellationToken cancellationToken) where T : AVObject; - } -} diff --git a/Storage/Source/Internal/Session/Controller/AVSessionController.cs b/Storage/Source/Internal/Session/Controller/AVSessionController.cs deleted file mode 100644 index fd40711..0000000 --- a/Storage/Source/Internal/Session/Controller/AVSessionController.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using LeanCloud.Storage.Internal; - -namespace LeanCloud.Storage.Internal { - public class AVSessionController : IAVSessionController { - private readonly IAVCommandRunner commandRunner; - - public AVSessionController(IAVCommandRunner commandRunner) { - this.commandRunner = commandRunner; - } - - public Task GetSessionAsync(string sessionToken, CancellationToken cancellationToken) { - var command = new AVCommand("sessions/me", - method: "GET", - sessionToken: sessionToken, - data: null); - - return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t => { - return AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance); - }); - } - - public Task RevokeAsync(string sessionToken, CancellationToken cancellationToken) { - var command = new AVCommand("logout", - method: "POST", - sessionToken: sessionToken, - data: new Dictionary()); - - return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken); - } - - public Task UpgradeToRevocableSessionAsync(string sessionToken, CancellationToken cancellationToken) { - var command = new AVCommand("upgradeToRevocableSession", - method: "POST", - sessionToken: sessionToken, - data: new Dictionary()); - - return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t => { - return AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance); - }); - } - - public bool IsRevocableSessionToken(string sessionToken) { - return sessionToken.Contains("r:"); - } - } -} diff --git a/Storage/Source/Internal/Session/Controller/IAVSessionController.cs b/Storage/Source/Internal/Session/Controller/IAVSessionController.cs deleted file mode 100644 index 72e7e95..0000000 --- a/Storage/Source/Internal/Session/Controller/IAVSessionController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal { - public interface IAVSessionController { - Task GetSessionAsync(string sessionToken, CancellationToken cancellationToken); - - Task RevokeAsync(string sessionToken, CancellationToken cancellationToken); - - Task UpgradeToRevocableSessionAsync(string sessionToken, CancellationToken cancellationToken); - - bool IsRevocableSessionToken(string sessionToken); - } -} diff --git a/Storage/Source/Internal/Storage/IStorageController.cs b/Storage/Source/Internal/Storage/IStorageController.cs deleted file mode 100644 index b334c92..0000000 --- a/Storage/Source/Internal/Storage/IStorageController.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal { - /// - /// An abstraction for accessing persistent storage in the LeanCloud SDK. - /// - public interface IStorageController { - /// - /// Load the contents of this storage controller asynchronously. - /// - /// - Task> LoadAsync(); - - /// - /// Overwrites the contents of this storage controller asynchronously. - /// - /// - /// - Task> SaveAsync(IDictionary contents); - } - - /// - /// An interface for a dictionary that is persisted to disk asynchronously. - /// - /// They key type of the dictionary. - /// The value type of the dictionary. - public interface IStorageDictionary : IEnumerable> { - int Count { get; } - TValue this[TKey key] { get; } - - IEnumerable Keys { get; } - IEnumerable Values { get; } - - bool ContainsKey(TKey key); - bool TryGetValue(TKey key, out TValue value); - - /// - /// Adds a key to this dictionary, and saves it asynchronously. - /// - /// The key to insert. - /// The value to insert. - /// - Task AddAsync(TKey key, TValue value); - - /// - /// Removes a key from this dictionary, and saves it asynchronously. - /// - /// - /// - Task RemoveAsync(TKey key); - } -} \ No newline at end of file diff --git a/Storage/Source/Internal/Storage/NetCore/StorageController.cs b/Storage/Source/Internal/Storage/NetCore/StorageController.cs deleted file mode 100644 index 87d537c..0000000 --- a/Storage/Source/Internal/Storage/NetCore/StorageController.cs +++ /dev/null @@ -1,184 +0,0 @@ -using System; -using System.Threading.Tasks; -using System.Linq; -using System.Collections.Generic; -using System.Threading; - -namespace LeanCloud.Storage.Internal -{ - /// - /// Implements `IStorageController` for PCL targets, based off of PCLStorage. - /// - public class StorageController : IStorageController - { - private class StorageDictionary : IStorageDictionary - { - private object mutex; - private Dictionary dictionary; - - public StorageDictionary() - { - this.mutex = new Object(); - dictionary = new Dictionary(); - } - - internal Task SaveAsync() - { - string json; - lock (mutex) - { - json = Json.Encode(dictionary); - } - return Task.FromResult(json); - } - - internal Task LoadAsync() - { - return Task.FromResult("{}").ContinueWith(t => - { - string text = t.Result; - Dictionary result = null; - try - { - result = Json.Parse(text) as Dictionary; - } - catch (Exception) - { - // Do nothing, JSON error. Probaby was empty string. - } - - lock (mutex) - { - dictionary = result ?? new Dictionary(); - } - }); - - } - - internal void Update(IDictionary contents) - { - lock (mutex) - { - dictionary = contents.ToDictionary(p => p.Key, p => p.Value); - } - } - - public Task AddAsync(string key, object value) - { - lock (mutex) - { - dictionary[key] = value; - } - return SaveAsync(); - } - - public Task RemoveAsync(string key) - { - lock (mutex) - { - dictionary.Remove(key); - } - return SaveAsync(); - } - - public bool ContainsKey(string key) - { - lock (mutex) - { - return dictionary.ContainsKey(key); - } - } - - public IEnumerable Keys - { - get { lock (mutex) { return dictionary.Keys; } } - } - - public bool TryGetValue(string key, out object value) - { - lock (mutex) - { - return dictionary.TryGetValue(key, out value); - } - } - - public IEnumerable Values - { - get { lock (mutex) { return dictionary.Values; } } - } - - public object this[string key] - { - get { lock (mutex) { return dictionary[key]; } } - } - - public int Count - { - get { lock (mutex) { return dictionary.Count; } } - } - - public IEnumerator> GetEnumerator() - { - lock (mutex) - { - return dictionary.GetEnumerator(); - } - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - lock (mutex) - { - return dictionary.GetEnumerator(); - } - } - } - - private const string AVStorageFileName = "ApplicationSettings"; - private readonly TaskQueue taskQueue = new TaskQueue(); - - private StorageDictionary storageDictionary; - - public StorageController() - { - - } - public StorageController(string appId) - { - - } - public Task> LoadAsync() - { - return taskQueue.Enqueue(toAwait => - { - return toAwait.ContinueWith(_ => - { - if (storageDictionary != null) - { - return Task.FromResult>(storageDictionary); - } - - storageDictionary = new StorageDictionary(); - return storageDictionary.LoadAsync().OnSuccess(__ => storageDictionary as IStorageDictionary); - }).Unwrap(); - }, CancellationToken.None); - } - - public Task> SaveAsync(IDictionary contents) - { - return taskQueue.Enqueue(toAwait => - { - return toAwait.ContinueWith(_ => - { - if (storageDictionary == null) - { - storageDictionary = new StorageDictionary(); - } - - storageDictionary.Update(contents); - return storageDictionary.SaveAsync().OnSuccess(__ => storageDictionary as IStorageDictionary); - }).Unwrap(); - }, CancellationToken.None); - } - } -} diff --git a/Storage/Source/Internal/Storage/Portable/StorageController.cs b/Storage/Source/Internal/Storage/Portable/StorageController.cs deleted file mode 100644 index 0390ed7..0000000 --- a/Storage/Source/Internal/Storage/Portable/StorageController.cs +++ /dev/null @@ -1,192 +0,0 @@ -using System; -using System.Threading.Tasks; -using System.Linq; -using PCLStorage; -using System.Collections.Generic; -using System.Threading; - -namespace LeanCloud.Storage.Internal -{ - /// - /// Implements `IStorageController` for PCL targets, based off of PCLStorage. - /// - public class StorageController : IStorageController - { - private class StorageDictionary : IStorageDictionary - { - private object mutex; - private Dictionary dictionary; - private IFile file; - - public StorageDictionary(IFile file) - { - this.file = file; - - mutex = new Object(); - dictionary = new Dictionary(); - } - - internal Task SaveAsync() - { - string json; - lock (mutex) - { - json = Json.Encode(dictionary); - } - return file.WriteAllTextAsync(json); - } - - internal Task LoadAsync() - { - return file.ReadAllTextAsync().ContinueWith(t => - { - string text = t.Result; - Dictionary result = null; - try - { - result = Json.Parse(text) as Dictionary; - } - catch (Exception) - { - // Do nothing, JSON error. Probaby was empty string. - } - - lock (mutex) - { - dictionary = result ?? new Dictionary(); - } - }); - } - - internal void Update(IDictionary contents) - { - lock (mutex) - { - dictionary = contents.ToDictionary(p => p.Key, p => p.Value); - } - } - - public Task AddAsync(string key, object value) - { - lock (mutex) - { - dictionary[key] = value; - } - return SaveAsync(); - } - - public Task RemoveAsync(string key) - { - lock (mutex) - { - dictionary.Remove(key); - } - return SaveAsync(); - } - - public bool ContainsKey(string key) - { - lock (mutex) - { - return dictionary.ContainsKey(key); - } - } - - public IEnumerable Keys - { - get { lock (mutex) { return dictionary.Keys; } } - } - - public bool TryGetValue(string key, out object value) - { - lock (mutex) - { - return dictionary.TryGetValue(key, out value); - } - } - - public IEnumerable Values - { - get { lock (mutex) { return dictionary.Values; } } - } - - public object this[string key] - { - get { lock (mutex) { return dictionary[key]; } } - } - - public int Count - { - get { lock (mutex) { return dictionary.Count; } } - } - - public IEnumerator> GetEnumerator() - { - lock (mutex) - { - return dictionary.GetEnumerator(); - } - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - lock (mutex) - { - return dictionary.GetEnumerator(); - } - } - } - - private const string LeanCloudStorageFileName = "ApplicationSettings"; - private readonly TaskQueue taskQueue = new TaskQueue(); - private readonly Task fileTask; - private StorageDictionary storageDictionary; - - public StorageController(string fileNamePrefix) - { - fileTask = taskQueue.Enqueue(t => t.ContinueWith(_ => - { - return FileSystem.Current.LocalStorage.CreateFileAsync(string.Format("{0}_{1}", fileNamePrefix, LeanCloudStorageFileName), CreationCollisionOption.OpenIfExists); - }).Unwrap(), CancellationToken.None); - } - - internal StorageController(IFile file) - { - this.fileTask = Task.FromResult(file); - } - - public Task> LoadAsync() - { - return taskQueue.Enqueue(toAwait => - { - return toAwait.ContinueWith(_ => - { - if (storageDictionary != null) - { - return Task.FromResult>(storageDictionary); - } - - storageDictionary = new StorageDictionary(fileTask.Result); - return storageDictionary.LoadAsync().OnSuccess(__ => storageDictionary as IStorageDictionary); - }).Unwrap(); - }, CancellationToken.None); - } - - public Task> SaveAsync(IDictionary contents) - { - return taskQueue.Enqueue(toAwait => - { - return toAwait.ContinueWith(_ => - { - if (storageDictionary == null) - { - storageDictionary = new StorageDictionary(fileTask.Result); - } - - storageDictionary.Update(contents); - return storageDictionary.SaveAsync().OnSuccess(__ => storageDictionary as IStorageDictionary); - }).Unwrap(); - }, CancellationToken.None); - } - } -} diff --git a/Storage/Source/Internal/Storage/Unity/StorageController.cs b/Storage/Source/Internal/Storage/Unity/StorageController.cs deleted file mode 100644 index 432a760..0000000 --- a/Storage/Source/Internal/Storage/Unity/StorageController.cs +++ /dev/null @@ -1,250 +0,0 @@ -using System; -using System.Threading.Tasks; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using UnityEngine; -using System.Threading; - -namespace LeanCloud.Storage.Internal -{ - /// - /// Implements `IStorageController` for PCL targets, based off of PCLStorage. - /// - public class StorageController : IStorageController - { - private const string LeanCloudStorageFileName = "LeanCloud.settings"; - - private TaskQueue taskQueue = new TaskQueue(); - private string settingsPath; - private StorageDictionary storageDictionary; - private bool isWebPlayer; - - private class StorageDictionary : IStorageDictionary - { - private object mutex; - private Dictionary dictionary; - private string settingsPath; - private bool isWebPlayer; - - public StorageDictionary(string settingsPath, bool isWebPlayer) - { - this.settingsPath = settingsPath; - this.isWebPlayer = isWebPlayer; - - mutex = new object(); - dictionary = new Dictionary(); - } - - internal Task SaveAsync() - { - string jsonEncoded; - lock (mutex) - { - jsonEncoded = Json.Encode(dictionary); - } - - if (this.isWebPlayer) - { - PlayerPrefs.SetString(LeanCloudStorageFileName, jsonEncoded); - PlayerPrefs.Save(); - } - else if (Application.platform == RuntimePlatform.tvOS) - { - Debug.Log("Running on TvOS, prefs cannot be saved."); - } - else - { - using (var fs = new FileStream(settingsPath, FileMode.Create, FileAccess.Write)) - { - using (var writer = new StreamWriter(fs)) - { - writer.Write(jsonEncoded); - } - } - } - - return Task.FromResult(null); - } - - internal Task LoadAsync() - { - string jsonString = null; - - try - { - if (this.isWebPlayer) - { - jsonString = PlayerPrefs.GetString(LeanCloudStorageFileName, null); - } - else if (Application.platform == RuntimePlatform.tvOS) - { - Debug.Log("Running on TvOS, prefs cannot be loaded."); - } - else - { - using (var fs = new FileStream(settingsPath, FileMode.Open, FileAccess.Read)) - { - var reader = new StreamReader(fs); - jsonString = reader.ReadToEnd(); - } - } - } - catch (Exception) - { - // Do nothing - } - - if (jsonString == null) - { - lock (mutex) - { - dictionary = new Dictionary(); - return Task.FromResult(null); - } - } - - Dictionary decoded = Json.Parse(jsonString) as Dictionary; - lock (mutex) - { - dictionary = decoded ?? new Dictionary(); - return Task.FromResult(null); - } - } - - internal void Update(IDictionary contents) - { - lock (mutex) - { - dictionary = contents.ToDictionary(p => p.Key, p => p.Value); - } - } - - public Task AddAsync(string key, object value) - { - lock (mutex) - { - dictionary[key] = value; - } - return SaveAsync(); - } - - public Task RemoveAsync(string key) - { - lock (mutex) - { - dictionary.Remove(key); - } - return SaveAsync(); - } - - public bool ContainsKey(string key) - { - lock (mutex) - { - return dictionary.ContainsKey(key); - } - } - - public IEnumerable Keys - { - get { lock (mutex) { return dictionary.Keys; } } - } - - public bool TryGetValue(string key, out object value) - { - lock (mutex) - { - return dictionary.TryGetValue(key, out value); - } - } - - public IEnumerable Values - { - get { lock (mutex) { return dictionary.Values; } } - } - - public object this[string key] - { - get { lock (mutex) { return dictionary[key]; } } - } - - public int Count - { - get { lock (mutex) { return dictionary.Count; } } - } - - public IEnumerator> GetEnumerator() - { - lock (mutex) - { - return dictionary.GetEnumerator(); - } - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - lock (mutex) - { - return dictionary.GetEnumerator(); - } - } - } - - public StorageController() - : this(Path.Combine(Application.persistentDataPath, LeanCloudStorageFileName), false) - { - - } - - public StorageController(String settingsPath) - : this(settingsPath, false) - { - - } - public StorageController(bool isWebPlayer, string fileNamePrefix) - : this(Path.Combine(Application.persistentDataPath, string.Format("{0}_{1}", fileNamePrefix, LeanCloudStorageFileName)), isWebPlayer) - { - - } - - public StorageController(string settingsPath, bool isWebPlayer) - { - this.settingsPath = settingsPath; - this.isWebPlayer = isWebPlayer; - } - - public Task> LoadAsync() - { - return taskQueue.Enqueue(toAwait => - { - return toAwait.ContinueWith(_ => - { - if (storageDictionary != null) - { - return Task.FromResult>(storageDictionary); - } - storageDictionary = new StorageDictionary(settingsPath, this.isWebPlayer); - return storageDictionary.LoadAsync().OnSuccess(__ => storageDictionary as IStorageDictionary); - }).Unwrap(); - }, CancellationToken.None); - } - - public Task> SaveAsync(IDictionary contents) - { - return taskQueue.Enqueue(toAwait => - { - return toAwait.ContinueWith(_ => - { - if (storageDictionary == null) - { - storageDictionary = new StorageDictionary(settingsPath, this.isWebPlayer); - } - - storageDictionary.Update(contents); - return storageDictionary.SaveAsync().OnSuccess(__ => storageDictionary as IStorageDictionary); - }).Unwrap(); - }, CancellationToken.None); - } - } -} diff --git a/Storage/Source/Internal/User/Controller/AVCurrentUserController.cs b/Storage/Source/Internal/User/Controller/AVCurrentUserController.cs deleted file mode 100644 index eb78961..0000000 --- a/Storage/Source/Internal/User/Controller/AVCurrentUserController.cs +++ /dev/null @@ -1,159 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Globalization; -using System.Threading; -using System.Threading.Tasks; -using LeanCloud.Storage.Internal; - -namespace LeanCloud.Storage.Internal -{ - public class AVCurrentUserController : IAVCurrentUserController - { - private readonly object mutex = new object(); - private readonly TaskQueue taskQueue = new TaskQueue(); - - private IStorageController storageController; - - public AVCurrentUserController(IStorageController storageController) - { - this.storageController = storageController; - } - - private AVUser currentUser; - public AVUser CurrentUser - { - get - { - lock (mutex) - { - return currentUser; - } - } - set - { - lock (mutex) - { - currentUser = value; - } - } - } - - public Task SetAsync(AVUser user, CancellationToken cancellationToken) - { - Task saveTask = null; - if (user == null) - { - saveTask = storageController - .LoadAsync() - .OnSuccess(t => t.Result.RemoveAsync("CurrentUser")) - .Unwrap(); - } - else - { - var data = user.ServerDataToJSONObjectForSerialization(); - data["objectId"] = user.ObjectId; - if (user.CreatedAt != null) - { - data["createdAt"] = user.CreatedAt.Value.ToString(AVClient.DateFormatStrings.First(), - CultureInfo.InvariantCulture); - } - if (user.UpdatedAt != null) - { - data["updatedAt"] = user.UpdatedAt.Value.ToString(AVClient.DateFormatStrings.First(), - CultureInfo.InvariantCulture); - } - - saveTask = storageController - .LoadAsync() - .OnSuccess(t => t.Result.AddAsync("CurrentUser", Json.Encode(data))) - .Unwrap(); - } - CurrentUser = user; - - return saveTask; - } - - public Task GetAsync(CancellationToken cancellationToken) - { - AVUser cachedCurrent; - - lock (mutex) - { - cachedCurrent = CurrentUser; - } - - if (cachedCurrent != null) - { - return Task.FromResult(cachedCurrent); - } - - return storageController.LoadAsync().OnSuccess(t => - { - object temp; - t.Result.TryGetValue("CurrentUser", out temp); - var userDataString = temp as string; - AVUser user = null; - if (userDataString != null) - { - var userData = Json.Parse(userDataString) as IDictionary; - var state = AVObjectCoder.Instance.Decode(userData, AVDecoder.Instance); - user = AVObject.FromState(state, "_User"); - } - - CurrentUser = user; - return user; - }); - } - - public Task ExistsAsync(CancellationToken cancellationToken) - { - if (CurrentUser != null) - { - return Task.FromResult(true); - } - - return storageController.LoadAsync().OnSuccess(t => t.Result.ContainsKey("CurrentUser")); - } - - public bool IsCurrent(AVUser user) - { - lock (mutex) - { - return CurrentUser == user; - } - } - - public void ClearFromMemory() - { - CurrentUser = null; - } - - public void ClearFromDisk() - { - lock (mutex) - { - ClearFromMemory(); - - storageController.LoadAsync().OnSuccess(t => t.Result.RemoveAsync("CurrentUser")); - } - } - - public Task GetCurrentSessionTokenAsync(CancellationToken cancellationToken) - { - return GetAsync(cancellationToken).OnSuccess(t => - { - var user = t.Result; - return user == null ? null : user.SessionToken; - }); - } - - public Task LogOutAsync(CancellationToken cancellationToken) - { - return GetAsync(cancellationToken).OnSuccess(t => - { - ClearFromDisk(); - }); - } - } -} diff --git a/Storage/Source/Internal/User/Controller/AVUserController.cs b/Storage/Source/Internal/User/Controller/AVUserController.cs deleted file mode 100644 index c74f602..0000000 --- a/Storage/Source/Internal/User/Controller/AVUserController.cs +++ /dev/null @@ -1,161 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using LeanCloud.Storage.Internal; - -namespace LeanCloud.Storage.Internal -{ - public class AVUserController : IAVUserController - { - private readonly IAVCommandRunner commandRunner; - - public AVUserController(IAVCommandRunner commandRunner) - { - this.commandRunner = commandRunner; - } - - public Task SignUpAsync(IObjectState state, - IDictionary operations, - CancellationToken cancellationToken) - { - var objectJSON = AVObject.ToJSONObjectForSaving(operations); - - var command = new AVCommand("classes/_User", - method: "POST", - data: objectJSON); - - return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t => - { - var serverState = AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance); - serverState = serverState.MutatedClone(mutableClone => - { - mutableClone.IsNew = true; - }); - return serverState; - }); - } - - public Task LogInAsync(string username, string email, - string password, - CancellationToken cancellationToken) - { - var data = new Dictionary{ - { "password", password} - }; - if (username != null) { - data.Add("username", username); - } - if (email != null) { - data.Add("email", email); - } - - var command = new AVCommand("login", - method: "POST", - data: data); - - return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t => - { - var serverState = AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance); - serverState = serverState.MutatedClone(mutableClone => - { - mutableClone.IsNew = t.Result.Item1 == System.Net.HttpStatusCode.Created; - }); - return serverState; - }); - } - - public Task LogInAsync(string authType, - IDictionary data, - bool failOnNotExist, - CancellationToken cancellationToken) - { - var authData = new Dictionary(); - authData[authType] = data; - var path = failOnNotExist ? "users?failOnNotExist=true" : "users"; - var command = new AVCommand(path, - method: "POST", - data: new Dictionary { - { "authData", authData} - }); - - return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t => - { - var serverState = AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance); - serverState = serverState.MutatedClone(mutableClone => - { - mutableClone.IsNew = t.Result.Item1 == System.Net.HttpStatusCode.Created; - }); - return serverState; - }); - } - - public Task GetUserAsync(string sessionToken, CancellationToken cancellationToken) - { - var command = new AVCommand("users/me", - method: "GET", - sessionToken: sessionToken, - data: null); - - return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t => - { - return AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance); - }); - } - - public Task RequestPasswordResetAsync(string email, CancellationToken cancellationToken) - { - var command = new AVCommand("requestPasswordReset", - method: "POST", - data: new Dictionary { - { "email", email} - }); - - return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken); - } - - public Task LogInWithParametersAsync(string relativeUrl, IDictionary data, - CancellationToken cancellationToken) - { - var command = new AVCommand(string.Format("{0}", relativeUrl), - method: "POST", - data: data); - - return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t => - { - var serverState = AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance); - serverState = serverState.MutatedClone(mutableClone => - { - mutableClone.IsNew = t.Result.Item1 == System.Net.HttpStatusCode.Created; - }); - return serverState; - }); - } - - public Task UpdatePasswordAsync(string userId, string sessionToken, string oldPassword, string newPassword, CancellationToken cancellationToken) - { - var command = new AVCommand(String.Format("users/{0}/updatePassword", userId), - method: "PUT", - sessionToken: sessionToken, - data: new Dictionary { - {"old_password", oldPassword}, - {"new_password", newPassword}, - }); - return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken); - } - - public Task RefreshSessionTokenAsync(string userId, string sessionToken, - CancellationToken cancellationToken) - { - var command = new AVCommand(String.Format("users/{0}/refreshSessionToken", userId), - method: "PUT", - sessionToken: sessionToken, - data: null); - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).OnSuccess(t => - { - var serverState = AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance); - return serverState; - }); - } - } -} diff --git a/Storage/Source/Internal/User/Controller/IAVCurrentUserController.cs b/Storage/Source/Internal/User/Controller/IAVCurrentUserController.cs deleted file mode 100644 index c7664aa..0000000 --- a/Storage/Source/Internal/User/Controller/IAVCurrentUserController.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal { - public interface IAVCurrentUserController : IAVObjectCurrentController { - Task GetCurrentSessionTokenAsync(CancellationToken cancellationToken); - - Task LogOutAsync(CancellationToken cancellationToken); - } -} diff --git a/Storage/Source/Internal/User/Controller/IAVUserController.cs b/Storage/Source/Internal/User/Controller/IAVUserController.cs deleted file mode 100644 index 58966a7..0000000 --- a/Storage/Source/Internal/User/Controller/IAVUserController.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal -{ - public interface IAVUserController - { - Task SignUpAsync(IObjectState state, - IDictionary operations, - CancellationToken cancellationToken); - - Task LogInAsync(string username, - string email, - string password, - CancellationToken cancellationToken); - - Task LogInWithParametersAsync(string relativeUrl, - IDictionary data, - CancellationToken cancellationToken); - - Task LogInAsync(string authType, - IDictionary data, - bool failOnNotExist, - CancellationToken cancellationToken); - - Task GetUserAsync(string sessionToken, - CancellationToken cancellationToken); - - Task RequestPasswordResetAsync(string email, - CancellationToken cancellationToken); - - Task UpdatePasswordAsync(string usedId, string sessionToken, - string oldPassword, string newPassword, - CancellationToken cancellationToken); - - Task RefreshSessionTokenAsync(string userId, - string sessionToken, - CancellationToken cancellationToken); - } -} diff --git a/Storage/Source/Internal/Utilities/AVConfigExtensions.cs b/Storage/Source/Internal/Utilities/AVConfigExtensions.cs deleted file mode 100644 index 0df396d..0000000 --- a/Storage/Source/Internal/Utilities/AVConfigExtensions.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace LeanCloud.Storage.Internal { - /// - /// So here's the deal. We have a lot of internal APIs for AVObject, AVUser, etc. - /// - /// These cannot be 'internal' anymore if we are fully modularizing things out, because - /// they are no longer a part of the same library, especially as we create things like - /// Installation inside push library. - /// - /// So this class contains a bunch of extension methods that can live inside another - /// namespace, which 'wrap' the intenral APIs that already exist. - /// - public static class AVConfigExtensions { - public static AVConfig Create(IDictionary fetchedConfig) { - return new AVConfig(fetchedConfig); - } - } -} diff --git a/Storage/Source/Internal/Utilities/AVFileExtensions.cs b/Storage/Source/Internal/Utilities/AVFileExtensions.cs deleted file mode 100644 index ae502d5..0000000 --- a/Storage/Source/Internal/Utilities/AVFileExtensions.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace LeanCloud.Storage.Internal { - /// - /// So here's the deal. We have a lot of internal APIs for AVObject, AVUser, etc. - /// - /// These cannot be 'internal' anymore if we are fully modularizing things out, because - /// they are no longer a part of the same library, especially as we create things like - /// Installation inside push library. - /// - /// So this class contains a bunch of extension methods that can live inside another - /// namespace, which 'wrap' the intenral APIs that already exist. - /// - public static class AVFileExtensions { - public static AVFile Create(string name, Uri uri, string mimeType = null) { - return new AVFile(name, uri, mimeType); - } - } -} diff --git a/Storage/Source/Internal/Utilities/AVObjectExtensions.cs b/Storage/Source/Internal/Utilities/AVObjectExtensions.cs deleted file mode 100644 index 2147049..0000000 --- a/Storage/Source/Internal/Utilities/AVObjectExtensions.cs +++ /dev/null @@ -1,225 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq.Expressions; -using System.Linq; -using System.Globalization; -using System.ComponentModel; -using System.Collections; - -namespace LeanCloud.Storage.Internal -{ - /// - /// So here's the deal. We have a lot of internal APIs for AVObject, AVUser, etc. - /// - /// These cannot be 'internal' anymore if we are fully modularizing things out, because - /// they are no longer a part of the same library, especially as we create things like - /// Installation inside push library. - /// - /// So this class contains a bunch of extension methods that can live inside another - /// namespace, which 'wrap' the intenral APIs that already exist. - /// - public static class AVObjectExtensions - { - public static T FromState(IObjectState state, string defaultClassName) where T : AVObject - { - return AVObject.FromState(state, defaultClassName); - } - - public static IObjectState GetState(this AVObject obj) - { - return obj.State; - } - - public static void HandleFetchResult(this AVObject obj, IObjectState serverState) - { - obj.HandleFetchResult(serverState); - } - - public static IDictionary GetCurrentOperations(this AVObject obj) - { - return obj.CurrentOperations; - } - - public static IDictionary Encode(this AVObject obj) - { - return PointerOrLocalIdEncoder.Instance.EncodeAVObject(obj, false); - } - - public static IEnumerable DeepTraversal(object root, bool traverseAVObjects = false, bool yieldRoot = false) - { - return AVObject.DeepTraversal(root, traverseAVObjects, yieldRoot); - } - - public static void SetIfDifferent(this AVObject obj, string key, T value) - { - obj.SetIfDifferent(key, value); - } - - public static IDictionary ServerDataToJSONObjectForSerialization(this AVObject obj) - { - return obj.ServerDataToJSONObjectForSerialization(); - } - - public static void Set(this AVObject obj, string key, object value) - { - obj.Set(key, value); - } - - public static void DisableHooks(this AVObject obj, IEnumerable hookKeys) - { - obj.Set("__ignore_hooks", hookKeys); - } - public static void DisableHook(this AVObject obj, string hookKey) - { - var newList = new List(); - if (obj.ContainsKey("__ignore_hooks")) - { - var hookKeys = obj.Get>("__ignore_hooks"); - newList = hookKeys.ToList(); - } - newList.Add(hookKey); - obj.DisableHooks(newList); - } - - public static void DisableAfterHook(this AVObject obj) - { - obj.DisableAfterSave(); - obj.DisableAfterUpdate(); - obj.DisableAfterDelete(); - } - - public static void DisableBeforeHook(this AVObject obj) - { - obj.DisableBeforeSave(); - obj.DisableBeforeDelete(); - obj.DisableBeforeUpdate(); - } - - public static void DisableBeforeSave(this AVObject obj) - { - obj.DisableHook("beforeSave"); - } - public static void DisableAfterSave(this AVObject obj) - { - obj.DisableHook("afterSave"); - } - public static void DisableBeforeUpdate(this AVObject obj) - { - obj.DisableHook("beforeUpdate"); - } - public static void DisableAfterUpdate(this AVObject obj) - { - obj.DisableHook("afterUpdate"); - } - public static void DisableBeforeDelete(this AVObject obj) - { - obj.DisableHook("beforeDelete"); - } - public static void DisableAfterDelete(this AVObject obj) - { - obj.DisableHook("afterDelete"); - } - - #region on property updated or changed or collection updated - - /// - /// On the property changed. - /// - /// Av object. - /// Property name. - /// Handler. - public static void OnPropertyChanged(this AVObject avObj, string propertyName, PropertyChangedEventHandler handler) - { - avObj.PropertyChanged += (sender, e) => - { - if (e.PropertyName == propertyName) - { - handler(sender, e); - } - }; - } - - /// - /// On the property updated. - /// - /// Av object. - /// Property name. - /// Handler. - public static void OnPropertyUpdated(this AVObject avObj, string propertyName, PropertyUpdatedEventHandler handler) - { - avObj.PropertyUpdated += (sender, e) => - { - if (e.PropertyName == propertyName) - { - handler(sender, e); - } - }; - } - - /// - /// On the property updated. - /// - /// Av object. - /// Property name. - /// Handler. - public static void OnPropertyUpdated(this AVObject avObj, string propertyName, Action handler) - { - avObj.OnPropertyUpdated(propertyName,(object sender, PropertyUpdatedEventArgs e) => - { - handler(e.OldValue, e.NewValue); - }); - } - - /// - /// On the collection property updated. - /// - /// Av object. - /// Property name. - /// Handler. - public static void OnCollectionPropertyUpdated(this AVObject avObj, string propertyName, CollectionPropertyUpdatedEventHandler handler) - { - avObj.CollectionPropertyUpdated += (sender, e) => - { - if (e.PropertyName == propertyName) - { - handler(sender, e); - } - }; - } - - /// - /// On the collection property added. - /// - /// Av object. - /// Property name. - /// Handler. - public static void OnCollectionPropertyAdded(this AVObject avObj, string propertyName, Action handler) - { - avObj.OnCollectionPropertyUpdated(propertyName, (sender, e) => - { - if (e.CollectionAction == NotifyCollectionUpdatedAction.Add) - { - handler(e.NewValues); - } - }); - } - - /// - /// On the collection property removed. - /// - /// Av object. - /// Property name. - /// Handler. - public static void OnCollectionPropertyRemoved(this AVObject avObj, string propertyName, Action handler) - { - avObj.OnCollectionPropertyUpdated(propertyName, (sender, e) => - { - if (e.CollectionAction == NotifyCollectionUpdatedAction.Remove) - { - handler(e.NewValues); - } - }); - } - #endregion - } -} diff --git a/Storage/Source/Internal/Utilities/AVQueryExtensions.cs b/Storage/Source/Internal/Utilities/AVQueryExtensions.cs deleted file mode 100644 index 56cdb2c..0000000 --- a/Storage/Source/Internal/Utilities/AVQueryExtensions.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace LeanCloud.Storage.Internal { - /// - /// So here's the deal. We have a lot of internal APIs for AVObject, AVUser, etc. - /// - /// These cannot be 'internal' anymore if we are fully modularizing things out, because - /// they are no longer a part of the same library, especially as we create things like - /// Installation inside push library. - /// - /// So this class contains a bunch of extension methods that can live inside another - /// namespace, which 'wrap' the intenral APIs that already exist. - /// - public static class AVQueryExtensions { - public static string GetClassName(this AVQuery query) where T: AVObject { - return query.ClassName; - } - - public static IDictionary BuildParameters(this AVQuery query) where T: AVObject { - return query.BuildParameters(false); - } - - public static object GetConstraint(this AVQuery query, string key) where T : AVObject { - return query.GetConstraint(key); - } - } -} diff --git a/Storage/Source/Internal/Utilities/AVRelationExtensions.cs b/Storage/Source/Internal/Utilities/AVRelationExtensions.cs deleted file mode 100644 index c2d2c85..0000000 --- a/Storage/Source/Internal/Utilities/AVRelationExtensions.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace LeanCloud.Storage.Internal { - /// - /// So here's the deal. We have a lot of internal APIs for AVObject, AVUser, etc. - /// - /// These cannot be 'internal' anymore if we are fully modularizing things out, because - /// they are no longer a part of the same library, especially as we create things like - /// Installation inside push library. - /// - /// So this class contains a bunch of extension methods that can live inside another - /// namespace, which 'wrap' the intenral APIs that already exist. - /// - public static class AVRelationExtensions { - public static AVRelation Create(AVObject parent, string childKey) where T : AVObject { - return new AVRelation(parent, childKey); - } - - public static AVRelation Create(AVObject parent, string childKey, string targetClassName) where T: AVObject { - return new AVRelation(parent, childKey, targetClassName); - } - - public static string GetTargetClassName(this AVRelation relation) where T : AVObject { - return relation.TargetClassName; - } - } -} diff --git a/Storage/Source/Internal/Utilities/AVSessionExtensions.cs b/Storage/Source/Internal/Utilities/AVSessionExtensions.cs deleted file mode 100644 index 40a3c3d..0000000 --- a/Storage/Source/Internal/Utilities/AVSessionExtensions.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal { - /// - /// So here's the deal. We have a lot of internal APIs for AVObject, AVUser, etc. - /// - /// These cannot be 'internal' anymore if we are fully modularizing things out, because - /// they are no longer a part of the same library, especially as we create things like - /// Installation inside push library. - /// - /// So this class contains a bunch of extension methods that can live inside another - /// namespace, which 'wrap' the intenral APIs that already exist. - /// - public static class AVSessionExtensions { - public static Task UpgradeToRevocableSessionAsync(string sessionToken, CancellationToken cancellationToken) { - return AVSession.UpgradeToRevocableSessionAsync(sessionToken, cancellationToken); - } - - public static Task RevokeAsync(string sessionToken, CancellationToken cancellationToken) { - return AVSession.RevokeAsync(sessionToken, cancellationToken); - } - } -} diff --git a/Storage/Source/Internal/Utilities/AVUserExtensions.cs b/Storage/Source/Internal/Utilities/AVUserExtensions.cs deleted file mode 100644 index 936dc37..0000000 --- a/Storage/Source/Internal/Utilities/AVUserExtensions.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal -{ - /// - /// So here's the deal. We have a lot of internal APIs for AVObject, AVUser, etc. - /// - /// These cannot be 'internal' anymore if we are fully modularizing things out, because - /// they are no longer a part of the same library, especially as we create things like - /// Installation inside push library. - /// - /// So this class contains a bunch of extension methods that can live inside another - /// namespace, which 'wrap' the intenral APIs that already exist. - /// - public static class AVUserExtensions - { - - } -} diff --git a/Storage/Source/Internal/Utilities/FlexibleDictionaryWrapper.cs b/Storage/Source/Internal/Utilities/FlexibleDictionaryWrapper.cs deleted file mode 100644 index 5ee7d2c..0000000 --- a/Storage/Source/Internal/Utilities/FlexibleDictionaryWrapper.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using LeanCloud.Utilities; - -namespace LeanCloud.Storage.Internal { - /// - /// Provides a Dictionary implementation that can delegate to any other - /// dictionary, regardless of its value type. Used for coercion of - /// dictionaries when returning them to users. - /// - /// The resulting type of value in the dictionary. - /// The original type of value in the dictionary. - [Preserve(AllMembers = true, Conditional = false)] - public class FlexibleDictionaryWrapper : IDictionary { - private readonly IDictionary toWrap; - public FlexibleDictionaryWrapper(IDictionary toWrap) { - this.toWrap = toWrap; - } - - public void Add(string key, TOut value) { - toWrap.Add(key, (TIn)Conversion.ConvertTo(value)); - } - - public bool ContainsKey(string key) { - return toWrap.ContainsKey(key); - } - - public ICollection Keys { - get { return toWrap.Keys; } - } - - public bool Remove(string key) { - return toWrap.Remove(key); - } - - public bool TryGetValue(string key, out TOut value) { - TIn outValue; - bool result = toWrap.TryGetValue(key, out outValue); - value = (TOut)Conversion.ConvertTo(outValue); - return result; - } - - public ICollection Values { - get { - return toWrap.Values - .Select(item => (TOut)Conversion.ConvertTo(item)).ToList(); - } - } - - public TOut this[string key] { - get { - return (TOut)Conversion.ConvertTo(toWrap[key]); - } - set { - toWrap[key] = (TIn)Conversion.ConvertTo(value); - } - } - - public void Add(KeyValuePair item) { - toWrap.Add(new KeyValuePair(item.Key, - (TIn)Conversion.ConvertTo(item.Value))); - } - - public void Clear() { - toWrap.Clear(); - } - - public bool Contains(KeyValuePair item) { - return toWrap.Contains(new KeyValuePair(item.Key, - (TIn)Conversion.ConvertTo(item.Value))); - } - - public void CopyTo(KeyValuePair[] array, int arrayIndex) { - var converted = from pair in toWrap - select new KeyValuePair(pair.Key, - (TOut)Conversion.ConvertTo(pair.Value)); - converted.ToList().CopyTo(array, arrayIndex); - } - - public int Count { - get { return toWrap.Count; } - } - - public bool IsReadOnly { - get { return toWrap.IsReadOnly; } - } - - public bool Remove(KeyValuePair item) { - return toWrap.Remove(new KeyValuePair(item.Key, - (TIn)Conversion.ConvertTo(item.Value))); - } - - public IEnumerator> GetEnumerator() { - foreach (var pair in toWrap) { - yield return new KeyValuePair(pair.Key, - (TOut)Conversion.ConvertTo(pair.Value)); - } - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { - return this.GetEnumerator(); - } - } -} diff --git a/Storage/Source/Internal/Utilities/FlexibleListWrapper.cs b/Storage/Source/Internal/Utilities/FlexibleListWrapper.cs deleted file mode 100644 index 3db78d6..0000000 --- a/Storage/Source/Internal/Utilities/FlexibleListWrapper.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using LeanCloud.Utilities; - -namespace LeanCloud.Storage.Internal { - /// - /// Provides a List implementation that can delegate to any other - /// list, regardless of its value type. Used for coercion of - /// lists when returning them to users. - /// - /// The resulting type of value in the list. - /// The original type of value in the list. - [Preserve(AllMembers = true, Conditional = false)] - public class FlexibleListWrapper : IList { - private IList toWrap; - public FlexibleListWrapper(IList toWrap) { - this.toWrap = toWrap; - } - - public int IndexOf(TOut item) { - return toWrap.IndexOf((TIn)Conversion.ConvertTo(item)); - } - - public void Insert(int index, TOut item) { - toWrap.Insert(index, (TIn)Conversion.ConvertTo(item)); - } - - public void RemoveAt(int index) { - toWrap.RemoveAt(index); - } - - public TOut this[int index] { - get { - return (TOut)Conversion.ConvertTo(toWrap[index]); - } - set { - toWrap[index] = (TIn)Conversion.ConvertTo(value); - } - } - - public void Add(TOut item) { - toWrap.Add((TIn)Conversion.ConvertTo(item)); - } - - public void Clear() { - toWrap.Clear(); - } - - public bool Contains(TOut item) { - return toWrap.Contains((TIn)Conversion.ConvertTo(item)); - } - - public void CopyTo(TOut[] array, int arrayIndex) { - toWrap.Select(item => (TOut)Conversion.ConvertTo(item)) - .ToList().CopyTo(array, arrayIndex); - } - - public int Count { - get { return toWrap.Count; } - } - - public bool IsReadOnly { - get { return toWrap.IsReadOnly; } - } - - public bool Remove(TOut item) { - return toWrap.Remove((TIn)Conversion.ConvertTo(item)); - } - - public IEnumerator GetEnumerator() { - foreach (var item in (IEnumerable)toWrap) { - yield return (TOut)Conversion.ConvertTo(item); - } - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { - return this.GetEnumerator(); - } - } -} diff --git a/Storage/Source/Internal/Utilities/IJsonConvertible.cs b/Storage/Source/Internal/Utilities/IJsonConvertible.cs deleted file mode 100644 index af29ef1..0000000 --- a/Storage/Source/Internal/Utilities/IJsonConvertible.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace LeanCloud.Storage.Internal { - /// - /// Represents an object that can be converted into JSON. - /// - public interface IJsonConvertible { - /// - /// Converts the object to a data structure that can be converted to JSON. - /// - /// An object to be JSONified. - IDictionary ToJSON(); - } -} diff --git a/Storage/Source/Internal/Utilities/IdentityEqualityComparer.cs b/Storage/Source/Internal/Utilities/IdentityEqualityComparer.cs deleted file mode 100644 index e25f818..0000000 --- a/Storage/Source/Internal/Utilities/IdentityEqualityComparer.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Collections.Generic; -using System.Runtime.CompilerServices; - -namespace LeanCloud.Storage.Internal -{ - /// - /// An equality comparer that uses the object identity (i.e. ReferenceEquals) - /// rather than .Equals, allowing identity to be used for checking equality in - /// ISets and IDictionaries. - /// - public class IdentityEqualityComparer : IEqualityComparer - { - public bool Equals(T x, T y) - { - return object.ReferenceEquals(x, y); - } - - public int GetHashCode(T obj) - { - return RuntimeHelpers.GetHashCode(obj); - } - } -} diff --git a/Storage/Source/Internal/Utilities/InternalExtensions.cs b/Storage/Source/Internal/Utilities/InternalExtensions.cs deleted file mode 100644 index e0228a5..0000000 --- a/Storage/Source/Internal/Utilities/InternalExtensions.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Runtime.ExceptionServices; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal { - /// - /// Provides helper methods that allow us to use terser code elsewhere. - /// - public static class InternalExtensions { - /// - /// Ensures a task (even null) is awaitable. - /// - /// - /// - /// - public static Task Safe(this Task task) { - return task ?? Task.FromResult(default(T)); - } - - /// - /// Ensures a task (even null) is awaitable. - /// - /// - /// - public static Task Safe(this Task task) { - return task ?? Task.FromResult(null); - } - - public delegate void PartialAccessor(ref T arg); - - public static TValue GetOrDefault(this IDictionary self, - TKey key, - TValue defaultValue) { - TValue value; - if (self.TryGetValue(key, out value)) { - return value; - } - return defaultValue; - } - - public static bool CollectionsEqual(this IEnumerable a, IEnumerable b) { - return Object.Equals(a, b) || - (a != null && b != null && - a.SequenceEqual(b)); - } - - public static Task OnSuccess(this Task task, - Func, TResult> continuation) { - return ((Task)task).OnSuccess(t => continuation((Task)t)); - } - - public static Task OnSuccess(this Task task, Action> continuation) { - return task.OnSuccess((Func, object>)(t => { - continuation(t); - return null; - })); - } - - public static Task OnSuccess(this Task task, - Func continuation) { - return task.ContinueWith(t => { - if (t.IsFaulted) { - var ex = t.Exception.Flatten(); - if (ex.InnerExceptions.Count == 1) { - ExceptionDispatchInfo.Capture(ex.InnerExceptions[0]).Throw(); - } else { - ExceptionDispatchInfo.Capture(ex).Throw(); - } - // Unreachable - return Task.FromResult(default(TResult)); - } else if (t.IsCanceled) { - var tcs = new TaskCompletionSource(); - tcs.SetCanceled(); - return tcs.Task; - } else { - return Task.FromResult(continuation(t)); - } - }).Unwrap(); - } - - public static Task OnSuccess(this Task task, Action continuation) { - return task.OnSuccess((Func)(t => { - continuation(t); - return null; - })); - } - - public static Task WhileAsync(Func> predicate, Func body) { - Func iterate = null; - iterate = () => { - return predicate().OnSuccess(t => { - if (!t.Result) { - return Task.FromResult(0); - } - return body().OnSuccess(_ => iterate()).Unwrap(); - }).Unwrap(); - }; - return iterate(); - } - } -} diff --git a/Storage/Source/Internal/Utilities/Json.cs b/Storage/Source/Internal/Utilities/Json.cs deleted file mode 100644 index 8babc7f..0000000 --- a/Storage/Source/Internal/Utilities/Json.cs +++ /dev/null @@ -1,554 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal -{ - /// - /// A simple recursive-descent JSON Parser based on the grammar defined at http://www.json.org - /// and http://tools.ietf.org/html/rfc4627 - /// - public class Json - { - /// - /// Place at the start of a regex to force the match to begin wherever the search starts (i.e. - /// anchored at the index of the first character of the search, even when that search starts - /// in the middle of the string). - /// - private static readonly string startOfString = "\\G"; - private static readonly char startObject = '{'; - private static readonly char endObject = '}'; - private static readonly char startArray = '['; - private static readonly char endArray = ']'; - private static readonly char valueSeparator = ','; - private static readonly char nameSeparator = ':'; - private static readonly char[] falseValue = "false".ToCharArray(); - private static readonly char[] trueValue = "true".ToCharArray(); - private static readonly char[] nullValue = "null".ToCharArray(); - private static readonly Regex numberValue = new Regex(startOfString + - @"-?(?:0|[1-9]\d*)(?\.\d+)?(?(?:e|E)(?:-|\+)?\d+)?"); - private static readonly Regex stringValue = new Regex(startOfString + - "\"(?(?:[^\\\\\"]|(?\\\\(?:[\\\\\"/bfnrt]|u[0-9a-fA-F]{4})))*)\"", - RegexOptions.Multiline); - - private static readonly Regex escapePattern = new Regex("\\\\|\"|[\u0000-\u001F]"); - - private class JsonStringParser - { - public string Input { get; private set; } - - public char[] InputAsArray { get; private set; } - - private int currentIndex; - public int CurrentIndex - { - get - { - return currentIndex; - } - } - - public void Skip(int skip) - { - currentIndex += skip; - } - - public JsonStringParser(string input) - { - Input = input; - InputAsArray = input.ToCharArray(); - } - - /// - /// Parses JSON object syntax (e.g. '{}') - /// - internal bool AVObject(out object output) - { - output = null; - int initialCurrentIndex = CurrentIndex; - if (!Accept(startObject)) - { - return false; - } - var dict = new Dictionary(); - while (true) - { - object pairValue; - if (!ParseMember(out pairValue)) - { - break; - } - var pair = pairValue as Tuple; - dict[pair.Item1] = pair.Item2; - if (!Accept(valueSeparator)) - { - break; - } - } - if (!Accept(endObject)) - { - return false; - } - output = dict; - return true; - } - - /// - /// Parses JSON member syntax (e.g. '"keyname" : null') - /// - private bool ParseMember(out object output) - { - output = null; - object key; - if (!ParseString(out key)) - { - return false; - } - if (!Accept(nameSeparator)) - { - return false; - } - object value; - if (!ParseValue(out value)) - { - return false; - } - output = new Tuple((string)key, value); - return true; - } - - /// - /// Parses JSON array syntax (e.g. '[]') - /// - internal bool ParseArray(out object output) - { - output = null; - if (!Accept(startArray)) - { - return false; - } - var list = new List(); - while (true) - { - object value; - if (!ParseValue(out value)) - { - break; - } - list.Add(value); - if (!Accept(valueSeparator)) - { - break; - } - } - if (!Accept(endArray)) - { - return false; - } - output = list; - return true; - } - - /// - /// Parses a value (i.e. the right-hand side of an object member assignment or - /// an element in an array) - /// - private bool ParseValue(out object output) - { - if (Accept(falseValue)) - { - output = false; - return true; - } - else if (Accept(nullValue)) - { - output = null; - return true; - } - else if (Accept(trueValue)) - { - output = true; - return true; - } - return AVObject(out output) || - ParseArray(out output) || - ParseNumber(out output) || - ParseString(out output); - } - - /// - /// Parses a JSON string (e.g. '"foo\u1234bar\n"') - /// - private bool ParseString(out object output) - { - output = null; - Match m; - if (!Accept(stringValue, out m)) - { - return false; - } - // handle escapes: - int offset = 0; - var contentCapture = m.Groups["content"]; - var builder = new StringBuilder(contentCapture.Value); - foreach (Capture escape in m.Groups["escape"].Captures) - { - int index = (escape.Index - contentCapture.Index) - offset; - offset += escape.Length - 1; - builder.Remove(index + 1, escape.Length - 1); - switch (escape.Value[1]) - { - case '\"': - builder[index] = '\"'; - break; - case '\\': - builder[index] = '\\'; - break; - case '/': - builder[index] = '/'; - break; - case 'b': - builder[index] = '\b'; - break; - case 'f': - builder[index] = '\f'; - break; - case 'n': - builder[index] = '\n'; - break; - case 'r': - builder[index] = '\r'; - break; - case 't': - builder[index] = '\t'; - break; - case 'u': - builder[index] = (char)ushort.Parse(escape.Value.Substring(2), NumberStyles.AllowHexSpecifier); - break; - default: - throw new ArgumentException("Unexpected escape character in string: " + escape.Value); - } - } - output = builder.ToString(); - return true; - } - - /// - /// Parses a number. Returns a long if the number is an integer or has an exponent, - /// otherwise returns a double. - /// - private bool ParseNumber(out object output) - { - output = null; - Match m; - if (!Accept(numberValue, out m)) - { - return false; - } - if (m.Groups["frac"].Length > 0 || m.Groups["exp"].Length > 0) - { - // It's a double. - output = double.Parse(m.Value, CultureInfo.InvariantCulture); - return true; - } - else - { - int temp = 0; - if (int.TryParse(m.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out temp)) - { - output = temp; - return true; - } - output = long.Parse(m.Value, CultureInfo.InvariantCulture); - return true; - } - } - - /// - /// Matches the string to a regex, consuming part of the string and returning the match. - /// - private bool Accept(Regex matcher, out Match match) - { - match = matcher.Match(Input, CurrentIndex); - if (match.Success) - { - Skip(match.Length); - } - return match.Success; - } - - /// - /// Find the first occurrences of a character, consuming part of the string. - /// - private bool Accept(char condition) - { - int step = 0; - int strLen = InputAsArray.Length; - int currentStep = currentIndex; - char currentChar; - - // Remove whitespace - while (currentStep < strLen && - ((currentChar = InputAsArray[currentStep]) == ' ' || - currentChar == '\r' || - currentChar == '\t' || - currentChar == '\n')) - { - ++step; - ++currentStep; - } - - bool match = (currentStep < strLen) && (InputAsArray[currentStep] == condition); - if (match) - { - ++step; - ++currentStep; - - // Remove whitespace - while (currentStep < strLen && - ((currentChar = InputAsArray[currentStep]) == ' ' || - currentChar == '\r' || - currentChar == '\t' || - currentChar == '\n')) - { - ++step; - ++currentStep; - } - - Skip(step); - } - return match; - } - - /// - /// Find the first occurrences of a string, consuming part of the string. - /// - private bool Accept(char[] condition) - { - int step = 0; - int strLen = InputAsArray.Length; - int currentStep = currentIndex; - char currentChar; - - // Remove whitespace - while (currentStep < strLen && - ((currentChar = InputAsArray[currentStep]) == ' ' || - currentChar == '\r' || - currentChar == '\t' || - currentChar == '\n')) - { - ++step; - ++currentStep; - } - - bool strMatch = true; - for (int i = 0; currentStep < strLen && i < condition.Length; ++i, ++currentStep) - { - if (InputAsArray[currentStep] != condition[i]) - { - strMatch = false; - break; - } - } - - bool match = (currentStep < strLen) && strMatch; - if (match) - { - Skip(step + condition.Length); - } - return match; - } - } - - /// - /// Parses a JSON-text as defined in http://tools.ietf.org/html/rfc4627, returning an - /// IDictionary<string, object> or an IList<object> depending on whether - /// the value was an array or dictionary. Nested objects also match these types. - /// - public static object Parse(string input) - { - object output; - input = input.Trim(); - JsonStringParser parser = new JsonStringParser(input); - - if ((parser.AVObject(out output) || - parser.ParseArray(out output)) && - parser.CurrentIndex == input.Length) - { - return output; - } - throw new ArgumentException("Input JSON was invalid."); - } - - /// - /// Encodes a dictionary into a JSON string. Supports values that are - /// IDictionary<string, object>, IList<object>, strings, - /// nulls, and any of the primitive types. - /// - public static string Encode(IDictionary dict) - { - if (dict == null) - { - throw new ArgumentNullException(); - } - if (dict.Count == 0) - { - return "{}"; - } - var builder = new StringBuilder("{"); - foreach (var pair in dict) - { - builder.Append(Encode(pair.Key)); - builder.Append(":"); - builder.Append(Encode(pair.Value)); - builder.Append(","); - } - builder[builder.Length - 1] = '}'; - return builder.ToString(); - } - - /// - /// Encodes a list into a JSON string. Supports values that are - /// IDictionary<string, object>, IList<object>, strings, - /// nulls, and any of the primitive types. - /// - public static string Encode(IList list) - { - if (list == null) - { - throw new ArgumentNullException(); - } - if (list.Count == 0) - { - return "[]"; - } - var builder = new StringBuilder("["); - foreach (var item in list) - { - builder.Append(Encode(item)); - builder.Append(","); - } - builder[builder.Length - 1] = ']'; - return builder.ToString(); - } - - public static string Encode(IList strList) - { - if (strList == null) - { - throw new ArgumentNullException(); - } - if (strList.Count == 0) - { - return "[]"; - } - StringBuilder stringBuilder = new StringBuilder("["); - foreach (object obj in strList) - { - stringBuilder.Append(Json.Encode(obj)); - stringBuilder.Append(","); - } - stringBuilder[stringBuilder.Length - 1] = ']'; - return stringBuilder.ToString(); - } - - public static string Encode(IList> dicList) - { - if (dicList == null) - { - throw new ArgumentNullException(); - } - if (dicList.Count == 0) - { - return "[]"; - } - StringBuilder stringBuilder = new StringBuilder("["); - foreach (object obj in dicList) - { - stringBuilder.Append(Json.Encode(obj)); - stringBuilder.Append(","); - } - stringBuilder[stringBuilder.Length - 1] = ']'; - return stringBuilder.ToString(); - } - - /// - /// Encodes an object into a JSON string. - /// - public static string Encode(object obj) - { - var dict = obj as IDictionary; - if (dict != null) - { - return Encode(dict); - } - var list = obj as IList; - if (list != null) - { - return Encode(list); - } - var dicList = obj as IList>; - if (dicList != null) - { - return Encode(dicList); - } - var strLists = obj as IList; - if (strLists != null) - { - return Encode(strLists); - } - var str = obj as string; - if (str != null) - { - str = escapePattern.Replace(str, m => - { - switch (m.Value[0]) - { - case '\\': - return "\\\\"; - case '\"': - return "\\\""; - case '\b': - return "\\b"; - case '\f': - return "\\f"; - case '\n': - return "\\n"; - case '\r': - return "\\r"; - case '\t': - return "\\t"; - default: - return "\\u" + ((ushort)m.Value[0]).ToString("x4"); - } - }); - return "\"" + str + "\""; - } - if (obj == null) - { - return "null"; - } - if (obj is bool) - { - if ((bool)obj) - { - return "true"; - } - else - { - return "false"; - } - } - if (!obj.GetType().GetTypeInfo().IsPrimitive) - { - throw new ArgumentException("Unable to encode objects of type " + obj.GetType()); - } - return Convert.ToString(obj, CultureInfo.InvariantCulture); - } - } -} diff --git a/Storage/Source/Internal/Utilities/LockSet.cs b/Storage/Source/Internal/Utilities/LockSet.cs deleted file mode 100644 index 65e188c..0000000 --- a/Storage/Source/Internal/Utilities/LockSet.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal { - public class LockSet { - private static readonly ConditionalWeakTable stableIds = - new ConditionalWeakTable(); - private static long nextStableId = 0; - - private readonly IEnumerable mutexes; - - public LockSet(IEnumerable mutexes) { - this.mutexes = (from mutex in mutexes - orderby GetStableId(mutex) - select mutex).ToList(); - } - - public void Enter() { - foreach (var mutex in mutexes) { - Monitor.Enter(mutex); - } - } - - public void Exit() { - foreach (var mutex in mutexes) { - Monitor.Exit(mutex); - } - } - - private static IComparable GetStableId(object mutex) { - lock (stableIds) { - return stableIds.GetValue(mutex, k => nextStableId++); - } - } - } -} diff --git a/Storage/Source/Internal/Utilities/ReflectionHelpers.cs b/Storage/Source/Internal/Utilities/ReflectionHelpers.cs deleted file mode 100644 index 55c4833..0000000 --- a/Storage/Source/Internal/Utilities/ReflectionHelpers.cs +++ /dev/null @@ -1,123 +0,0 @@ -using System; -using System.Reflection; -using System.Collections.Generic; -using System.Linq; - -namespace LeanCloud.Storage.Internal -{ - public static class ReflectionHelpers - { - public static IEnumerable GetProperties(Type type) - { -#if MONO || UNITY - return type.GetProperties(); -#else - return type.GetRuntimeProperties(); -#endif - } - - public static MethodInfo GetMethod(Type type, string name, Type[] parameters) - { -#if MONO || UNITY - return type.GetMethod(name, parameters); -#else - return type.GetRuntimeMethod(name, parameters); -#endif - } - - public static bool IsPrimitive(Type type) - { -#if MONO || UNITY - return type.IsPrimitive; -#else - return type.GetTypeInfo().IsPrimitive; -#endif - } - - public static IEnumerable GetInterfaces(Type type) - { -#if MONO || UNITY - return type.GetInterfaces(); -#else - return type.GetTypeInfo().ImplementedInterfaces; -#endif - } - - public static bool IsConstructedGenericType(Type type) - { -#if UNITY - return type.IsGenericType && !type.IsGenericTypeDefinition; -#else - return type.IsConstructedGenericType; -#endif - } - - public static IEnumerable GetConstructors(Type type) - { -#if UNITY - BindingFlags searchFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; - return type.GetConstructors(searchFlags); -#else - return type.GetTypeInfo().DeclaredConstructors - .Where(c => (c.Attributes & MethodAttributes.Static) == 0); -#endif - } - - public static Type[] GetGenericTypeArguments(Type type) - { -#if UNITY - return type.GetGenericArguments(); -#else - return type.GenericTypeArguments; -#endif - } - - public static PropertyInfo GetProperty(Type type, string name) - { -#if MONO || UNITY - return type.GetProperty(name); -#else - return type.GetRuntimeProperty(name); -#endif - } - - /// - /// This method helps simplify the process of getting a constructor for a type. - /// A method like this exists in .NET but is not allowed in a Portable Class Library, - /// so we've built our own. - /// - /// - /// - /// - public static ConstructorInfo FindConstructor(this Type self, params Type[] parameterTypes) - { - var constructors = - from constructor in GetConstructors(self) - let parameters = constructor.GetParameters() - let types = from p in parameters select p.ParameterType - where types.SequenceEqual(parameterTypes) - select constructor; - return constructors.SingleOrDefault(); - } - - public static bool IsNullable(Type t) - { - bool isGeneric; -#if UNITY - isGeneric = t.IsGenericType && !t.IsGenericTypeDefinition; -#else - isGeneric = t.IsConstructedGenericType; -#endif - return isGeneric && t.GetGenericTypeDefinition().Equals(typeof(Nullable<>)); - } - - public static IEnumerable GetCustomAttributes(this Assembly assembly) where T : Attribute - { -#if UNITY - return assembly.GetCustomAttributes(typeof(T), false).Select(attr => attr as T); -#else - return CustomAttributeExtensions.GetCustomAttributes(assembly); -#endif - } - } -} diff --git a/Storage/Source/Internal/Utilities/SynchronizedEventHandler.cs b/Storage/Source/Internal/Utilities/SynchronizedEventHandler.cs deleted file mode 100644 index d94f12a..0000000 --- a/Storage/Source/Internal/Utilities/SynchronizedEventHandler.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal { - /// - /// Represents an event handler that calls back from the synchronization context - /// that subscribed. - /// Should look like an EventArgs, but may not inherit EventArgs if T is implemented by the Windows team. - /// - public class SynchronizedEventHandler { - private LinkedList> delegates = - new LinkedList>(); - public void Add(Delegate del) { - lock (delegates) { - TaskFactory factory; - if (SynchronizationContext.Current != null) { - factory = - new TaskFactory(CancellationToken.None, - TaskCreationOptions.None, - TaskContinuationOptions.ExecuteSynchronously, - TaskScheduler.FromCurrentSynchronizationContext()); - } else { - factory = Task.Factory; - } - foreach (var d in del.GetInvocationList()) { - delegates.AddLast(new Tuple(d, factory)); - } - } - } - - public void Remove(Delegate del) { - lock (delegates) { - if (delegates.Count == 0) { - return; - } - foreach (var d in del.GetInvocationList()) { - var node = delegates.First; - while (node != null) { - if (node.Value.Item1 == d) { - delegates.Remove(node); - break; - } - node = node.Next; - } - } - } - } - - public Task Invoke(object sender, T args) { - IEnumerable> toInvoke; - var toContinue = new[] { Task.FromResult(0) }; - lock (delegates) { - toInvoke = delegates.ToList(); - } - var invocations = toInvoke - .Select(p => p.Item2.ContinueWhenAll(toContinue, - _ => p.Item1.DynamicInvoke(sender, args))) - .ToList(); - return Task.WhenAll(invocations); - } - } -} diff --git a/Storage/Source/Internal/Utilities/TaskQueue.cs b/Storage/Source/Internal/Utilities/TaskQueue.cs deleted file mode 100644 index 4f1ee25..0000000 --- a/Storage/Source/Internal/Utilities/TaskQueue.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal { - /// - /// A helper class for enqueuing tasks - /// - public class TaskQueue { - /// - /// We only need to keep the tail of the queue. Cancelled tasks will - /// just complete normally/immediately when their turn arrives. - /// - private Task tail; - private readonly object mutex = new object(); - - /// - /// Gets a cancellable task that can be safely awaited and is dependent - /// on the current tail of the queue. This essentially gives us a proxy - /// for the tail end of the queue whose awaiting can be cancelled. - /// - /// A cancellation token that cancels - /// the task even if the task is still in the queue. This allows the - /// running task to return immediately without breaking the dependency - /// chain. It also ensures that errors do not propagate. - /// A new task that should be awaited by enqueued tasks. - private Task GetTaskToAwait(CancellationToken cancellationToken) { - lock (mutex) { - Task toAwait = tail ?? Task.FromResult(true); - return toAwait.ContinueWith(task => { }, cancellationToken); - } - } - - /// - /// Enqueues a task created by . If the task is - /// cancellable (or should be able to be cancelled while it is waiting in the - /// queue), pass a cancellationToken. - /// - /// The type of task. - /// A function given a task to await once state is - /// snapshotted (e.g. after capturing session tokens at the time of the save call). - /// Awaiting this task will wait for the created task's turn in the queue. - /// A cancellation token that can be used to - /// cancel waiting in the queue. - /// The task created by the taskStart function. - public T Enqueue(Func taskStart, CancellationToken cancellationToken) - where T : Task { - Task oldTail; - T task; - lock (mutex) { - oldTail = this.tail ?? Task.FromResult(true); - // The task created by taskStart is responsible for waiting the - // task passed to it before doing its work (this gives it an opportunity - // to do startup work or save state before waiting for its turn in the queue - task = taskStart(GetTaskToAwait(cancellationToken)); - - // The tail task should be dependent on the old tail as well as the newly-created - // task. This prevents cancellation of the new task from causing the queue to run - // out of order. - this.tail = Task.WhenAll(oldTail, task); - } - return task; - } - - public object Mutex { get { return mutex; } } - } -} diff --git a/Storage/Source/Internal/Utilities/XamarinAttributes.cs b/Storage/Source/Internal/Utilities/XamarinAttributes.cs deleted file mode 100644 index 219cb1a..0000000 --- a/Storage/Source/Internal/Utilities/XamarinAttributes.cs +++ /dev/null @@ -1,426 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; - -namespace LeanCloud.Storage.Internal { - /// - /// A reimplementation of Xamarin's PreserveAttribute. - /// This allows us to support AOT and linking for Xamarin platforms. - /// - [AttributeUsage(AttributeTargets.All)] - internal class PreserveAttribute : Attribute { - public bool AllMembers; - public bool Conditional; - } - - [AttributeUsage(AttributeTargets.All)] - internal class LinkerSafeAttribute : Attribute { - public LinkerSafeAttribute() { } - } - - [Preserve(AllMembers = true)] - internal class PreserveWrapperTypes { - /// - /// Exists to ensure that generic types are AOT-compiled for the conversions we support. - /// Any new value types that we add support for will need to be registered here. - /// The method itself is never called, but by virtue of the Preserve attribute being set - /// on the class, these types will be AOT-compiled. - /// - /// This also applies to Unity. - /// - private static List CreateWrapperTypes() { - return new List { - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - - (Action)(() => AVCloud.CallFunctionAsync(null, null, null,CancellationToken.None)), - (Action)(() => AVCloud.CallFunctionAsync(null, null,null, CancellationToken.None)), - (Action)(() => AVCloud.CallFunctionAsync(null, null,null, CancellationToken.None)), - (Action)(() => AVCloud.CallFunctionAsync(null, null,null ,CancellationToken.None)), - (Action)(() => AVCloud.CallFunctionAsync(null, null,null, CancellationToken.None)), - (Action)(() => AVCloud.CallFunctionAsync(null, null,null, CancellationToken.None)), - (Action)(() => AVCloud.CallFunctionAsync(null, null,null, CancellationToken.None)), - (Action)(() => AVCloud.CallFunctionAsync(null, null,null, CancellationToken.None)), - (Action)(() => AVCloud.CallFunctionAsync(null, null, null,CancellationToken.None)), - (Action)(() => AVCloud.CallFunctionAsync(null, null,null ,CancellationToken.None)), - (Action)(() => AVCloud.CallFunctionAsync(null, null,null, CancellationToken.None)), - (Action)(() => AVCloud.CallFunctionAsync(null, null,null, CancellationToken.None)), - (Action)(() => AVCloud.CallFunctionAsync(null, null,null, CancellationToken.None)), - (Action)(() => AVCloud.CallFunctionAsync>(null, null,null, CancellationToken.None)), - (Action)(() => AVCloud.CallFunctionAsync>(null, null,null, CancellationToken.None)), - - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - }; - } - } -} diff --git a/Storage/Source/Public/AVACL.cs b/Storage/Source/Public/AVACL.cs deleted file mode 100644 index 22544c4..0000000 --- a/Storage/Source/Public/AVACL.cs +++ /dev/null @@ -1,284 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using LeanCloud.Storage.Internal; - -namespace LeanCloud { - /// - /// A AVACL is used to control which users and roles can access or modify a particular object. Each - /// can have its own AVACL. You can grant read and write permissions - /// separately to specific users, to groups of users that belong to roles, or you can grant permissions - /// to "the public" so that, for example, any user could read a particular object but only a particular - /// set of users could write to that object. - /// - public class AVACL : IJsonConvertible { - private enum AccessKind { - Read, - Write - } - private const string publicName = "*"; - private readonly ICollection readers = new HashSet(); - private readonly ICollection writers = new HashSet(); - - internal AVACL(IDictionary jsonObject) { - readers = new HashSet(from pair in jsonObject - where ((IDictionary)pair.Value).ContainsKey("read") - select pair.Key); - writers = new HashSet(from pair in jsonObject - where ((IDictionary)pair.Value).ContainsKey("write") - select pair.Key); - } - - /// - /// Creates an ACL with no permissions granted. - /// - public AVACL() { - } - - /// - /// Creates an ACL where only the provided user has access. - /// - /// The only user that can read or write objects governed by this ACL. - public AVACL(AVUser owner) { - SetReadAccess(owner, true); - SetWriteAccess(owner, true); - } - - IDictionary IJsonConvertible.ToJSON() { - var result = new Dictionary(); - foreach (var user in readers.Union(writers)) { - var userPermissions = new Dictionary(); - if (readers.Contains(user)) { - userPermissions["read"] = true; - } - if (writers.Contains(user)) { - userPermissions["write"] = true; - } - result[user] = userPermissions; - } - return result; - } - - private void SetAccess(AccessKind kind, string userId, bool allowed) { - if (userId == null) { - throw new ArgumentException("Cannot set access for an unsaved user or role."); - } - ICollection target = null; - switch (kind) { - case AccessKind.Read: - target = readers; - break; - case AccessKind.Write: - target = writers; - break; - default: - throw new NotImplementedException("Unknown AccessKind"); - } - if (allowed) { - target.Add(userId); - } else { - target.Remove(userId); - } - } - - private bool GetAccess(AccessKind kind, string userId) { - if (userId == null) { - throw new ArgumentException("Cannot get access for an unsaved user or role."); - } - switch (kind) { - case AccessKind.Read: - return readers.Contains(userId); - case AccessKind.Write: - return writers.Contains(userId); - default: - throw new NotImplementedException("Unknown AccessKind"); - } - } - - /// - /// Gets or sets whether the public is allowed to read this object. - /// - public bool PublicReadAccess { - get { - return GetAccess(AccessKind.Read, publicName); - } - set { - SetAccess(AccessKind.Read, publicName, value); - } - } - - /// - /// Gets or sets whether the public is allowed to write this object. - /// - public bool PublicWriteAccess { - get { - return GetAccess(AccessKind.Write, publicName); - } - set { - SetAccess(AccessKind.Write, publicName, value); - } - } - - /// - /// Sets whether the given user id is allowed to read this object. - /// - /// The objectId of the user. - /// Whether the user has permission. - public void SetReadAccess(string userId, bool allowed) { - SetAccess(AccessKind.Read, userId, allowed); - } - - /// - /// Sets whether the given user is allowed to read this object. - /// - /// The user. - /// Whether the user has permission. - public void SetReadAccess(AVUser user, bool allowed) { - SetReadAccess(user.ObjectId, allowed); - } - - /// - /// Sets whether the given user id is allowed to write this object. - /// - /// The objectId of the user. - /// Whether the user has permission. - public void SetWriteAccess(string userId, bool allowed) { - SetAccess(AccessKind.Write, userId, allowed); - } - - /// - /// Sets whether the given user is allowed to write this object. - /// - /// The user. - /// Whether the user has permission. - public void SetWriteAccess(AVUser user, bool allowed) { - SetWriteAccess(user.ObjectId, allowed); - } - - /// - /// Gets whether the given user id is *explicitly* allowed to read this object. - /// Even if this returns false, the user may still be able to read it if - /// PublicReadAccess is true or a role that the user belongs to has read access. - /// - /// The user objectId to check. - /// Whether the user has access. - public bool GetReadAccess(string userId) { - return GetAccess(AccessKind.Read, userId); - } - - /// - /// Gets whether the given user is *explicitly* allowed to read this object. - /// Even if this returns false, the user may still be able to read it if - /// PublicReadAccess is true or a role that the user belongs to has read access. - /// - /// The user to check. - /// Whether the user has access. - public bool GetReadAccess(AVUser user) { - return GetReadAccess(user.ObjectId); - } - - /// - /// Gets whether the given user id is *explicitly* allowed to write this object. - /// Even if this returns false, the user may still be able to write it if - /// PublicReadAccess is true or a role that the user belongs to has write access. - /// - /// The user objectId to check. - /// Whether the user has access. - public bool GetWriteAccess(string userId) { - return GetAccess(AccessKind.Write, userId); - } - - /// - /// Gets whether the given user is *explicitly* allowed to write this object. - /// Even if this returns false, the user may still be able to write it if - /// PublicReadAccess is true or a role that the user belongs to has write access. - /// - /// The user to check. - /// Whether the user has access. - public bool GetWriteAccess(AVUser user) { - return GetWriteAccess(user.ObjectId); - } - - /// - /// Sets whether users belonging to the role with the given - /// are allowed to read this object. - /// - /// The name of the role. - /// Whether the role has access. - public void SetRoleReadAccess(string roleName, bool allowed) { - SetAccess(AccessKind.Read, "role:" + roleName, allowed); - } - - /// - /// Sets whether users belonging to the given role are allowed to read this object. - /// - /// The role. - /// Whether the role has access. - public void SetRoleReadAccess(AVRole role, bool allowed) { - SetRoleReadAccess(role.Name, allowed); - } - - /// - /// Gets whether users belonging to the role with the given - /// are allowed to read this object. Even if this returns false, the role may still be - /// able to read it if a parent role has read access. - /// - /// The name of the role. - /// Whether the role has access. - public bool GetRoleReadAccess(string roleName) { - return GetAccess(AccessKind.Read, "role:" + roleName); - } - - /// - /// Gets whether users belonging to the role are allowed to read this object. - /// Even if this returns false, the role may still be able to read it if a - /// parent role has read access. - /// - /// The name of the role. - /// Whether the role has access. - public bool GetRoleReadAccess(AVRole role) { - return GetRoleReadAccess(role.Name); - } - - /// - /// Sets whether users belonging to the role with the given - /// are allowed to write this object. - /// - /// The name of the role. - /// Whether the role has access. - public void SetRoleWriteAccess(string roleName, bool allowed) { - SetAccess(AccessKind.Write, "role:" + roleName, allowed); - } - - /// - /// Sets whether users belonging to the given role are allowed to write this object. - /// - /// The role. - /// Whether the role has access. - public void SetRoleWriteAccess(AVRole role, bool allowed) { - SetRoleWriteAccess(role.Name, allowed); - } - - /// - /// Gets whether users belonging to the role with the given - /// are allowed to write this object. Even if this returns false, the role may still be - /// able to write it if a parent role has write access. - /// - /// The name of the role. - /// Whether the role has access. - public bool GetRoleWriteAccess(string roleName) { - return GetAccess(AccessKind.Write, "role:" + roleName); - } - - /// - /// Gets whether users belonging to the role are allowed to write this object. - /// Even if this returns false, the role may still be able to write it if a - /// parent role has write access. - /// - /// The name of the role. - /// Whether the role has access. - public bool GetRoleWriteAccess(AVRole role) { - return GetRoleWriteAccess(role.Name); - } - } -} diff --git a/Storage/Source/Public/AVClassNameAttribute.cs b/Storage/Source/Public/AVClassNameAttribute.cs deleted file mode 100644 index aa82d0b..0000000 --- a/Storage/Source/Public/AVClassNameAttribute.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud -{ - /// - /// Defines the class name for a subclass of AVObject. - /// - [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)] - public sealed class AVClassNameAttribute : Attribute - { - /// - /// Constructs a new AVClassName attribute. - /// - /// The class name to associate with the AVObject subclass. - public AVClassNameAttribute(string className) - { - this.ClassName = className; - } - - /// - /// Gets the class name to associate with the AVObject subclass. - /// - public string ClassName { get; private set; } - } -} diff --git a/Storage/Source/Public/AVClient.cs b/Storage/Source/Public/AVClient.cs deleted file mode 100644 index d49f76a..0000000 --- a/Storage/Source/Public/AVClient.cs +++ /dev/null @@ -1,506 +0,0 @@ -using LeanCloud.Storage.Internal; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Net; -using System.Reflection; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud -{ - /// - /// AVClient contains static functions that handle global - /// configuration for the LeanCloud library. - /// - public static partial class AVClient - { - public static readonly string[] DateFormatStrings = { - // Official ISO format - "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'", - // It's possible that the string converter server-side may trim trailing zeroes, - // so these two formats cover ourselves from that. - "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'ff'Z'", - "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'f'Z'", - }; - - /// - /// Represents the configuration of the LeanCloud SDK. - /// - public struct Configuration - { - /// - /// 与 SDK 通讯的云端节点 - /// - public enum AVRegion - { - /// - /// 默认值,LeanCloud 华北节点,同 Public_North_China - /// - [Obsolete("please use Configuration.AVRegion.Public_North_China")] - Public_CN = 0, - - /// - /// 默认值,华北公有云节点,同 Public_CN - /// - Public_North_China = 0, - - /// - /// LeanCloud 北美区公有云节点,同 Public_North_America - /// - [Obsolete("please use Configuration.AVRegion.Public_North_America")] - Public_US = 1, - /// - /// LeanCloud 北美区公有云节点,同 Public_US - /// - Public_North_America = 1, - - /// - /// 华东公有云节点,同 Public_East_China - /// - [Obsolete("please use Configuration.AVRegion.Public_East_China")] - Vendor_Tencent = 2, - - /// - /// 华东公有云节点,同 Vendor_Tencent - /// - Public_East_China = 2, - } - - /// - /// In the event that you would like to use the LeanCloud SDK - /// from a completely portable project, with no platform-specific library required, - /// to get full access to all of our features available on LeanCloud.com - /// (A/B testing, slow queries, etc.), you must set the values of this struct - /// to be appropriate for your platform. - /// - /// Any values set here will overwrite those that are automatically configured by - /// any platform-specific migration library your app includes. - /// - public struct VersionInformation - { - /// - /// The build number of your app. - /// - public string BuildVersion { get; set; } - - /// - /// The human friendly version number of your happ. - /// - public string DisplayVersion { get; set; } - - /// - /// The operating system version of the platform the SDK is operating in.. - /// - public string OSVersion { get; set; } - - } - - /// - /// The LeanCloud application ID of your app. - /// - public string ApplicationId { get; set; } - - /// - /// LeanCloud C# SDK 支持的服务节点,目前支持华北,华东和北美公有云节点和私有节点,以及专属节点 - /// - public AVRegion Region { get; set; } - - internal int RegionValue - { - get - { - return (int)Region; - } - } - - /// - /// The LeanCloud application key for your app. - /// - public string ApplicationKey { get; set; } - - /// - /// The LeanCloud master key for your app. - /// - /// The master key. - public string MasterKey { get; set; } - - /// - /// Gets or sets additional HTTP headers to be sent with network requests from the SDK. - /// - public IDictionary AdditionalHTTPHeaders { get; set; } - - /// - /// The version information of your application environment. - /// - public VersionInformation VersionInfo { get; set; } - - /// - /// 存储服务器地址 - /// - /// The API server. - public string ApiServer { get; set; } - - /// - /// 云引擎服务器地址 - /// - /// The engine server uri. - public string EngineServer { get; set; } - - /// - /// 即时通信服务器地址 - /// - /// The RTMR outer. - public string RTMServer { get; set; } - - /// - /// 直连即时通信服务器地址 - /// - /// The realtime server. - public string RealtimeServer { get; set; } - - public Uri PushServer { get; set; } - - public Uri StatsServer { get; set; } - } - - private static readonly object mutex = new object(); - - static AVClient() - { - versionString = "net-portable-" + Version; - - //AVModuleController.Instance.ScanForModules(); - } - - /// - /// The current configuration that LeanCloud has been initialized with. - /// - public static Configuration CurrentConfiguration { get; internal set; } - - internal static Version Version - { - get - { - var assemblyName = new AssemblyName(typeof(AVClient).GetTypeInfo().Assembly.FullName); - return assemblyName.Version; - } - } - - private static readonly string versionString; - /// - /// 当前 SDK 版本号 - /// - public static string VersionString - { - get - { - return versionString; - } - } - - /// - /// Authenticates this client as belonging to your application. This must be - /// called before your application can use the LeanCloud library. The recommended - /// way is to put a call to AVClient.Initialize in your - /// Application startup. - /// - /// The Application ID provided in the LeanCloud dashboard. - /// - /// The .NET API Key provided in the LeanCloud dashboard. - /// - public static void Initialize(string applicationId, string applicationKey) - { - Initialize(new Configuration - { - ApplicationId = applicationId, - ApplicationKey = applicationKey - }); - } - - internal static Action LogTracker { get; private set; } - - /// - /// 启动日志打印 - /// - /// - public static void HttpLog(Action trace) - { - LogTracker = trace; - } - /// - /// 打印 HTTP 访问日志 - /// - /// - public static void PrintLog(string log) - { - if (AVClient.LogTracker != null) - { - AVClient.LogTracker(log); - } - } - - static bool useProduction = true; - /// - /// Gets or sets a value indicating whether send the request to production server or staging server. - /// - /// true if use production; otherwise, false. - public static bool UseProduction - { - get - { - return useProduction; - } - set - { - useProduction = value; - } - } - - static bool useMasterKey = false; - public static bool UseMasterKey - { - get - { - return useMasterKey; - } - set - { - useMasterKey = value; - } - } - - /// - /// Authenticates this client as belonging to your application. This must be - /// called before your application can use the LeanCloud library. The recommended - /// way is to put a call to AVClient.Initialize in your - /// Application startup. - /// - /// The configuration to initialize LeanCloud with. - /// - public static void Initialize(Configuration configuration) - { - Config(configuration); - - AVObject.RegisterSubclass(); - AVObject.RegisterSubclass(); - AVObject.RegisterSubclass(); - } - - internal static void Config(Configuration configuration) - { - lock (mutex) - { - var nodeHash = configuration.ApplicationId.Split('-'); - if (nodeHash.Length > 1) - { - if (nodeHash[1].Trim() == "9Nh9j0Va") - { - configuration.Region = Configuration.AVRegion.Public_East_China; - } - } - - CurrentConfiguration = configuration; - } - } - - internal static void Clear() - { - AVPlugins.Instance.AppRouterController.Clear(); - AVPlugins.Instance.Reset(); - AVUser.ClearInMemoryUser(); - } - - /// - /// Switch app. - /// - /// Configuration. - public static void Switch(Configuration configuration) - { - Clear(); - Initialize(configuration); - } - - public static void Switch(string applicationId, string applicationKey, Configuration.AVRegion region = Configuration.AVRegion.Public_North_China) - { - var configuration = new Configuration - { - ApplicationId = applicationId, - ApplicationKey = applicationKey, - Region = region - }; - Switch(configuration); - } - - public static string BuildQueryString(IDictionary parameters) - { - return string.Join("&", (from pair in parameters - let valueString = pair.Value as string - select string.Format("{0}={1}", - Uri.EscapeDataString(pair.Key), - Uri.EscapeDataString(string.IsNullOrEmpty(valueString) ? - Json.Encode(pair.Value) : valueString))) - .ToArray()); - } - - internal static IDictionary DecodeQueryString(string queryString) - { - var dict = new Dictionary(); - foreach (var pair in queryString.Split('&')) - { - var parts = pair.Split(new char[] { '=' }, 2); - dict[parts[0]] = parts.Length == 2 ? Uri.UnescapeDataString(parts[1].Replace("+", " ")) : null; - } - return dict; - } - - internal static IDictionary DeserializeJsonString(string jsonData) - { - return Json.Parse(jsonData) as IDictionary; - } - - internal static string SerializeJsonString(IDictionary jsonData) - { - return Json.Encode(jsonData); - } - - public static Task> HttpGetAsync(Uri uri) - { - return RequestAsync(uri, "GET", null, body: null, contentType: null, cancellationToken: CancellationToken.None); - } - - public static Task> RequestAsync(Uri uri, string method, IList> headers, IDictionary body, string contentType, CancellationToken cancellationToken) - { - var dataStream = body != null ? new MemoryStream(Encoding.UTF8.GetBytes(Json.Encode(body))) : null; - return AVClient.RequestAsync(uri, method, headers, dataStream, contentType, cancellationToken); - //return AVPlugins.Instance.HttpClient.ExecuteAsync(request, null, null, cancellationToken); - } - - public static Task> RequestAsync(Uri uri, string method, IList> headers, Stream data, string contentType, CancellationToken cancellationToken) - { - HttpRequest request = new HttpRequest() - { - Data = data != null ? data : null, - Headers = headers, - Method = method, - Uri = uri - }; - return AVPlugins.Instance.HttpClient.ExecuteAsync(request, null, null, cancellationToken).OnSuccess(t => - { - var response = t.Result; - var contentString = response.Item2; - int responseCode = (int)response.Item1; - var responseLog = responseCode + ";" + contentString; - PrintLog(responseLog); - return response; - }); - } - - internal static Tuple> ReponseResolve(Tuple response, CancellationToken cancellationToken) - { - Tuple result = response; - HttpStatusCode code = result.Item1; - string item2 = result.Item2; - - if (item2 == null) - { - cancellationToken.ThrowIfCancellationRequested(); - return new Tuple>(code, null); - } - IDictionary strs = null; - try - { - strs = (!item2.StartsWith("[", StringComparison.Ordinal) ? AVClient.DeserializeJsonString(item2) : new Dictionary() - { - { "results", Json.Parse(item2) } - }); - } - catch (Exception exception) - { - throw new AVException(AVException.ErrorCode.OtherCause, "Invalid response from server", exception); - } - var codeValue = (int)code; - if (codeValue > 203 || codeValue < 200) - { - throw new AVException((AVException.ErrorCode)((int)((strs.ContainsKey("code") ? (long)strs["code"] : (long)-1))), (strs.ContainsKey("error") ? strs["error"] as string : item2), null); - } - - cancellationToken.ThrowIfCancellationRequested(); - return new Tuple>(code, strs); - } - internal static Task>> RequestAsync(string method, Uri relativeUri, string sessionToken, IDictionary data, CancellationToken cancellationToken) - { - - var command = new AVCommand(relativeUri.ToString(), - method: method, - sessionToken: sessionToken, - data: data); - - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command, cancellationToken: cancellationToken); - } - - internal static Task>> RunCommandAsync(AVCommand command) - { - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command); - } - - internal static bool IsSuccessStatusCode(HttpStatusCode responseStatus) - { - var codeValue = (int)responseStatus; - return (codeValue > 199) && (codeValue < 204); - } - - public static string ToLog(this HttpRequest request) - { - StringBuilder sb = new StringBuilder(); - var start = "===HTTP Request Start==="; - sb.AppendLine(start); - var urlLog = "Url: " + request.Uri; - sb.AppendLine(urlLog); - - var methodLog = "Method: " + request.Method; - sb.AppendLine(methodLog); - - try - { - var headers = request.Headers.ToDictionary(x => x.Key, x => x.Value as object); - var headersLog = "Headers: " + Json.Encode(headers); - sb.AppendLine(headersLog); - } - catch (Exception) - { - - } - - try - { - if (request is AVCommand) - { - var command = (AVCommand)request; - if (command.DataObject != null) - { - var bodyLog = "Body:" + Json.Encode(command.DataObject); - sb.AppendLine(bodyLog); - } - } - else - { - StreamReader reader = new StreamReader(request.Data); - string bodyLog = reader.ReadToEnd(); - sb.AppendLine(bodyLog); - } - } - catch (Exception) - { - - } - - var end = "===HTTP Request End==="; - sb.AppendLine(end); - return sb.ToString(); - } - } -} diff --git a/Storage/Source/Public/AVCloud.cs b/Storage/Source/Public/AVCloud.cs deleted file mode 100644 index 6954bed..0000000 --- a/Storage/Source/Public/AVCloud.cs +++ /dev/null @@ -1,583 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using LeanCloud.Storage.Internal; - -namespace LeanCloud { - /// - /// The AVCloud class provides methods for interacting with LeanCloud Cloud Functions. - /// - /// - /// For example, this sample code calls the - /// "validateGame" Cloud Function and calls processResponse if the call succeeded - /// and handleError if it failed. - /// - /// - /// var result = - /// await AVCloud.CallFunctionAsync<IDictionary<string, object>>("validateGame", parameters); - /// - /// - public static class AVCloud - { - internal static IAVCloudCodeController CloudCodeController - { - get - { - return AVPlugins.Instance.CloudCodeController; - } - } - - /// - /// Calls a cloud function. - /// - /// The type of data you will receive from the cloud function. This - /// can be an IDictionary, string, IList, AVObject, or any other type supported by - /// AVObject. - /// The cloud function to call. - /// The parameters to send to the cloud function. This - /// dictionary can contain anything that could be passed into a AVObject except for - /// AVObjects themselves. - /// - /// The cancellation token. - /// The result of the cloud call. - public static Task CallFunctionAsync(string name, IDictionary parameters = null, string sesstionToken = null, CancellationToken cancellationToken = default) - { - var sessionTokenTask = AVUser.TakeSessionToken(sesstionToken); - - return sessionTokenTask.OnSuccess(s => - { - return CloudCodeController.CallFunctionAsync(name, - parameters, s.Result, - cancellationToken); - - }).Unwrap(); - } - - /// - /// 远程调用云函数,返回结果会反序列化为 . - /// - /// - /// - /// - /// - /// - /// - public static Task RPCFunctionAsync(string name, IDictionary parameters = null, string sesstionToken = null, CancellationToken cancellationToken = default) - { - var sessionTokenTask = AVUser.TakeSessionToken(sesstionToken); - - return sessionTokenTask.OnSuccess(s => - { - return CloudCodeController.RPCFunction(name, - parameters, - s.Result, - cancellationToken); - }).Unwrap(); - } - - /// - /// 获取 LeanCloud 服务器的时间 - /// - /// 如果获取失败,将返回 DateTime.MinValue - /// - /// - /// 服务器的时间 - public static Task GetServerDateTimeAsync() - { - var command = new AVCommand(relativeUri: "date", - method: "GET", - sessionToken: null, - data: null); - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t => - { - DateTime rtn = DateTime.MinValue; - if (AVClient.IsSuccessStatusCode(t.Result.Item1)) - { - var date = AVDecoder.Instance.Decode(t.Result.Item2); - if (date != null) - { - if (date is DateTime) - { - rtn = (DateTime)date; - } - } - } - return rtn; - }); - } - - /// - /// 请求发送验证码。 - /// - /// 是否发送成功。 - /// 手机号。 - /// 应用名称。 - /// 进行的操作名称。 - /// 验证码失效时间。 - /// Cancellation token。 - public static Task RequestSMSCodeAsync(string mobilePhoneNumber, string name, string op, int ttl = 10, CancellationToken cancellationToken = default) - { - if (string.IsNullOrEmpty(mobilePhoneNumber)) - { - throw new AVException(AVException.ErrorCode.MobilePhoneInvalid, "Moblie Phone number is invalid.", null); - } - - Dictionary strs = new Dictionary() - { - { "mobilePhoneNumber", mobilePhoneNumber }, - }; - if (!string.IsNullOrEmpty(name)) - { - strs.Add("name", name); - } - if (!string.IsNullOrEmpty(op)) - { - strs.Add("op", op); - } - if (ttl > 0) - { - strs.Add("TTL", ttl); - } - var command = new AVCommand("requestSmsCode", - method: "POST", - sessionToken: null, - data: strs); - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).ContinueWith(t => - { - return AVClient.IsSuccessStatusCode(t.Result.Item1); - }); - } - - /// - /// 请求发送验证码。 - /// - /// 是否发送成功。 - /// 手机号。 - public static Task RequestSMSCodeAsync(string mobilePhoneNumber) - { - return AVCloud.RequestSMSCodeAsync(mobilePhoneNumber, CancellationToken.None); - } - - - /// - /// 请求发送验证码。 - /// - /// 是否发送成功。 - /// 手机号。 - /// Cancellation Token. - public static Task RequestSMSCodeAsync(string mobilePhoneNumber, CancellationToken cancellationToken) - { - return AVCloud.RequestSMSCodeAsync(mobilePhoneNumber, null, null, 0, cancellationToken); - } - - /// - /// 发送手机短信,并指定模板以及传入模板所需的参数。 - /// Exceptions: - /// AVOSCloud.AVException: - /// 手机号为空。 - /// - /// Sms's template - /// Template variables env. - /// Sms's sign. - /// - public static Task RequestSMSCodeAsync( - string mobilePhoneNumber, - string template, - IDictionary env, - string sign = "", - string validateToken = "") - { - - if (string.IsNullOrEmpty(mobilePhoneNumber)) - { - throw new AVException(AVException.ErrorCode.MobilePhoneInvalid, "Moblie Phone number is invalid.", null); - } - Dictionary strs = new Dictionary() - { - { "mobilePhoneNumber", mobilePhoneNumber }, - }; - strs.Add("template", template); - if (string.IsNullOrEmpty(sign)) - { - strs.Add("sign", sign); - } - if (string.IsNullOrEmpty(validateToken)) - { - strs.Add("validate_token", validateToken); - } - foreach (var key in env.Keys) - { - strs.Add(key, env[key]); - } - var command = new AVCommand("requestSmsCode", - method: "POST", - sessionToken: null, - data: strs); - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t => - { - return AVClient.IsSuccessStatusCode(t.Result.Item1); - }); - } - - /// - /// - /// - /// - /// - public static Task RequestVoiceCodeAsync(string mobilePhoneNumber) - { - if (string.IsNullOrEmpty(mobilePhoneNumber)) - { - throw new AVException(AVException.ErrorCode.MobilePhoneInvalid, "Moblie Phone number is invalid.", null); - } - Dictionary strs = new Dictionary() - { - { "mobilePhoneNumber", mobilePhoneNumber }, - { "smsType", "voice" }, - { "IDD","+86" } - }; - - var command = new AVCommand("requestSmsCode", - method: "POST", - sessionToken: null, - data: strs); - - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t => - { - return AVClient.IsSuccessStatusCode(t.Result.Item1); - }); - } - - /// - /// 验证是否是有效短信验证码。 - /// - /// 是否验证通过。 - /// 手机号 - /// 验证码。 - public static Task VerifySmsCodeAsync(string code, string mobilePhoneNumber) - { - return AVCloud.VerifySmsCodeAsync(code, mobilePhoneNumber, CancellationToken.None); - } - - /// - /// 验证是否是有效短信验证码。 - /// - /// 是否验证通过。 - /// 验证码。 - /// 手机号 - /// Cancellation token. - public static Task VerifySmsCodeAsync(string code, string mobilePhoneNumber, CancellationToken cancellationToken) - { - var command = new AVCommand("verifySmsCode/" + code.Trim() + "?mobilePhoneNumber=" + mobilePhoneNumber.Trim(), - method: "POST", - sessionToken: null, - data: null); - - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).ContinueWith(t => - { - return AVClient.IsSuccessStatusCode(t.Result.Item1); - }); - } - - /// - /// Stands for a captcha result. - /// - public class Captcha - { - /// - /// Used for captcha verify. - /// - public string Token { internal set; get; } - - /// - /// Captcha image URL. - /// - public string Url { internal set; get; } - - /// - /// Verify the user's input of catpcha. - /// - /// User's input of this captcha. - /// CancellationToken. - /// - public Task VerifyAsync(string code) - { - return AVCloud.VerifyCaptchaAsync(code, Token); - } - } - - /// - /// Get a captcha image. - /// - /// captcha image width. - /// captcha image height. - /// CancellationToken. - /// an instance of Captcha. - public static Task RequestCaptchaAsync(int width = 85, int height = 30, CancellationToken cancellationToken = default) - { - var path = string.Format("requestCaptcha?width={0}&height={1}", width, height); - var command = new AVCommand(path, "GET", null, data: null); - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t => - { - var decoded = AVDecoder.Instance.Decode(t.Result.Item2) as IDictionary; - return new Captcha() - { - Token = decoded["captcha_token"] as string, - Url = decoded["captcha_url"] as string, - }; - }); - } - - /// - /// Verify the user's input of catpcha. - /// - /// The captcha's token, from server. - /// User's input of this captcha. - /// CancellationToken. - /// - public static Task VerifyCaptchaAsync(string code, string token, CancellationToken cancellationToken = default) - { - var data = new Dictionary - { - { "captcha_token", token }, - { "captcha_code", code }, - }; - var command = new AVCommand("verifyCaptcha", "POST", null, data: data); - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).ContinueWith(t => - { - if (!t.Result.Item2.ContainsKey("validate_token")) - throw new KeyNotFoundException("validate_token"); - return t.Result.Item2["validate_token"] as string; - }); - } - - /// - /// Get the custom cloud parameters, you can set them at console https://leancloud.cn/dashboard/devcomponent.html?appid={your_app_Id}#/component/custom_param - /// - /// - /// - public static Task> GetCustomParametersAsync(CancellationToken cancellationToken = default) - { - var command = new AVCommand(string.Format("statistics/apps/{0}/sendPolicy", AVClient.CurrentConfiguration.ApplicationId), - method: "GET", - sessionToken: null, - data: null); - - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t => - { - var settings = t.Result.Item2; - var CloudParameters = settings["parameters"] as IDictionary; - return CloudParameters; - }); - } - - public class RealtimeSignature - { - public string Nonce { internal set; get; } - public long Timestamp { internal set; get; } - public string ClientId { internal set; get; } - public string Signature { internal set; get; } - } - - public static Task RequestRealtimeSignatureAsync(CancellationToken cancellationToken = default) - { - return AVUser.GetCurrentUserAsync(cancellationToken).OnSuccess(t => - { - return RequestRealtimeSignatureAsync(t.Result, cancellationToken); - }).Unwrap(); - } - - public static Task RequestRealtimeSignatureAsync(AVUser user, CancellationToken cancellationToken = default) - { - var command = new AVCommand(string.Format("rtm/sign"), - method: "POST", - sessionToken: null, - data: new Dictionary - { - { "session_token", user.SessionToken }, - } - ); - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).ContinueWith(t => - { - var body = t.Result.Item2; - return new RealtimeSignature() - { - Nonce = body["nonce"] as string, - Timestamp = (long)body["timestamp"], - ClientId = body["client_id"] as string, - Signature = body["signature"] as string, - }; - }); - } - - /// - /// Gets the LeanEngine hosting URL for current app async. - /// - /// The lean engine hosting URL async. - public static Task GetLeanEngineHostingUrlAsync() - { - return CallFunctionAsync("_internal_extensions_get_domain"); - } - - } - - - /// - /// AVRPCC loud function base. - /// - public class AVRPCCloudFunctionBase - { - /// - /// AVRPCD eserialize. - /// - public delegate R AVRPCDeserialize(IDictionary result); - /// - /// AVRPCS erialize. - /// - public delegate IDictionary AVRPCSerialize

(P parameters); - - public AVRPCCloudFunctionBase() - : this(true) - { - - } - - public AVRPCCloudFunctionBase(bool noneParameters) - { - if (noneParameters) - { - this.Encode = n => - { - return null; - }; - } - } - - - - private AVRPCDeserialize _decode; - public AVRPCDeserialize Decode - { - get - { - return _decode; - } - set - { - _decode = value; - } - } - - - private AVRPCSerialize

_encode; - public AVRPCSerialize

Encode - { - get - { - if (_encode == null) - { - _encode = n => - { - if (n != null) - return Json.Parse(n.ToString()) as IDictionary; - return null; - }; - } - return _encode; - } - set - { - _encode = value; - } - - } - public string FunctionName { get; set; } - - public Task ExecuteAsync(P parameters) - { - return AVUser.GetCurrentAsync().OnSuccess(t => - { - var user = t.Result; - var encodedParameters = Encode(parameters); - var command = new AVCommand( - string.Format("call/{0}", Uri.EscapeUriString(FunctionName)), - method: "POST", - sessionToken: user != null ? user.SessionToken : null, - data: encodedParameters); - - return AVClient.RunCommandAsync(command); - - }).Unwrap().OnSuccess(s => - { - var responseBody = s.Result.Item2; - if (!responseBody.ContainsKey("result")) - { - return default(R); - } - - return Decode(responseBody); - }); - - } - } - - - public class AVObjectRPCCloudFunction : AVObjectRPCCloudFunction - { - - } - public class AVObjectListRPCCloudFunction : AVObjectListRPCCloudFunction - { - - } - - public class AVObjectListRPCCloudFunction : AVRPCCloudFunctionBase> where R : AVObject - { - public AVObjectListRPCCloudFunction() - : base(true) - { - this.Decode = this.AVObjectListDeserializer(); - } - - public AVRPCDeserialize> AVObjectListDeserializer() - { - AVRPCDeserialize> del = data => - { - var items = data["result"] as IList; - - return items.Select(item => - { - var state = AVObjectCoder.Instance.Decode(item as IDictionary, AVDecoder.Instance); - return AVObject.FromState(state, state.ClassName); - }).ToList() as IList; - - }; - return del; - } - } - - public class AVObjectRPCCloudFunction : AVRPCCloudFunctionBase where R : AVObject - { - public AVObjectRPCCloudFunction() - : base(true) - { - this.Decode = this.AVObjectDeserializer(); - } - - - public AVRPCDeserialize AVObjectDeserializer() - { - AVRPCDeserialize del = data => - { - var item = data["result"] as object; - var state = AVObjectCoder.Instance.Decode(item as IDictionary, AVDecoder.Instance); - - return AVObject.FromState(state, state.ClassName); - }; - return del; - - } - } -} diff --git a/Storage/Source/Public/AVConfig.cs b/Storage/Source/Public/AVConfig.cs deleted file mode 100644 index 8e3b041..0000000 --- a/Storage/Source/Public/AVConfig.cs +++ /dev/null @@ -1,143 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Threading; -using System.Threading.Tasks; -using LeanCloud.Storage.Internal; -using LeanCloud.Utilities; - -namespace LeanCloud -{ - /// - /// The AVConfig is a representation of the remote configuration object, - /// that enables you to add things like feature gating, a/b testing or simple "Message of the day". - /// - public class AVConfig : IJsonConvertible - { - private IDictionary properties = new Dictionary(); - - /// - /// Gets the latest fetched AVConfig. - /// - /// AVConfig object - public static AVConfig CurrentConfig - { - get - { - Task task = ConfigController.CurrentConfigController.GetCurrentConfigAsync(); - task.Wait(); - return task.Result; - } - } - - internal static void ClearCurrentConfig() - { - ConfigController.CurrentConfigController.ClearCurrentConfigAsync().Wait(); - } - - internal static void ClearCurrentConfigInMemory() - { - ConfigController.CurrentConfigController.ClearCurrentConfigInMemoryAsync().Wait(); - } - - private static IAVConfigController ConfigController - { - get { return AVPlugins.Instance.ConfigController; } - } - - internal AVConfig() - : base() - { - } - - internal AVConfig(IDictionary fetchedConfig) - { - var props = AVDecoder.Instance.Decode(fetchedConfig["params"]) as IDictionary; - properties = props; - } - - /// - /// Retrieves the AVConfig asynchronously from the server. - /// - /// AVConfig object that was fetched - public static Task GetAsync() - { - return GetAsync(CancellationToken.None); - } - - /// - /// Retrieves the AVConfig asynchronously from the server. - /// - /// The cancellation token. - /// AVConfig object that was fetched - public static Task GetAsync(CancellationToken cancellationToken) - { - return ConfigController.FetchConfigAsync(AVUser.CurrentSessionToken, cancellationToken); - } - - /// - /// Gets a value for the key of a particular type. - /// - /// The type to convert the value to. Supported types are - /// AVObject and its descendents, LeanCloud types such as AVRelation and AVGeopoint, - /// primitive types,IList<T>, IDictionary<string, T> and strings. - /// The key of the element to get. - /// The property is retrieved - /// and is not found. - /// The property under this - /// key was found, but of a different type. - public T Get(string key) - { - return Conversion.To(this.properties[key]); - } - - /// - /// Populates result with the value for the key, if possible. - /// - /// The desired type for the value. - /// The key to retrieve a value for. - /// The value for the given key, converted to the - /// requested type, or null if unsuccessful. - /// true if the lookup and conversion succeeded, otherwise false. - public bool TryGetValue(string key, out T result) - { - if (this.properties.ContainsKey(key)) - { - try - { - var temp = Conversion.To(this.properties[key]); - result = temp; - return true; - } - catch (Exception) - { - // Could not convert, do nothing - } - } - result = default(T); - return false; - } - - /// - /// Gets a value on the config. - /// - /// The key for the parameter. - /// The property is - /// retrieved and is not found. - /// The value for the key. - virtual public object this[string key] - { - get - { - return this.properties[key]; - } - } - - IDictionary IJsonConvertible.ToJSON() - { - return new Dictionary { - { "params", NoObjectsEncoder.Instance.Encode(properties) } - }; - } - } -} diff --git a/Storage/Source/Public/AVDownloadProgressEventArgs.cs b/Storage/Source/Public/AVDownloadProgressEventArgs.cs deleted file mode 100644 index 8858eaa..0000000 --- a/Storage/Source/Public/AVDownloadProgressEventArgs.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace LeanCloud { - /// - /// Represents download progress. - /// - public class AVDownloadProgressEventArgs : EventArgs { - public AVDownloadProgressEventArgs() { } - - /// - /// Gets the progress (a number between 0.0 and 1.0) of a download. - /// - public double Progress { get; set; } - } -} diff --git a/Storage/Source/Public/AVException.cs b/Storage/Source/Public/AVException.cs deleted file mode 100644 index 4fedef0..0000000 --- a/Storage/Source/Public/AVException.cs +++ /dev/null @@ -1,275 +0,0 @@ -using System; - -namespace LeanCloud -{ - /// - /// Exceptions that may occur when sending requests to LeanCloud. - /// - public class AVException : Exception - { - /// - /// Error codes that may be delivered in response to requests to LeanCloud. - /// - public enum ErrorCode - { - /// - /// Error code indicating that an unknown error or an error unrelated to LeanCloud - /// occurred. - /// - OtherCause = -1, - - /// - /// Error code indicating that something has gone wrong with the server. - /// If you get this error code, it is LeanCloud's fault. - /// - InternalServerError = 1, - - /// - /// Error code indicating the connection to the LeanCloud servers failed. - /// - ConnectionFailed = 100, - - /// - /// Error code indicating the specified object doesn't exist. - /// - ObjectNotFound = 101, - - /// - /// Error code indicating you tried to query with a datatype that doesn't - /// support it, like exact matching an array or object. - /// - InvalidQuery = 102, - - /// - /// Error code indicating a missing or invalid classname. Classnames are - /// case-sensitive. They must start with a letter, and a-zA-Z0-9_ are the - /// only valid characters. - /// - InvalidClassName = 103, - - /// - /// Error code indicating an unspecified object id. - /// - MissingObjectId = 104, - - /// - /// Error code indicating an invalid key name. Keys are case-sensitive. They - /// must start with a letter, and a-zA-Z0-9_ are the only valid characters. - /// - InvalidKeyName = 105, - - /// - /// Error code indicating a malformed pointer. You should not see this unless - /// you have been mucking about changing internal LeanCloud code. - /// - InvalidPointer = 106, - - /// - /// Error code indicating that badly formed JSON was received upstream. This - /// either indicates you have done something unusual with modifying how - /// things encode to JSON, or the network is failing badly. - /// - InvalidJSON = 107, - - /// - /// Error code indicating that the feature you tried to access is only - /// available internally for testing purposes. - /// - CommandUnavailable = 108, - - /// - /// You must call LeanCloud.initialize before using the LeanCloud library. - /// - NotInitialized = 109, - - /// - /// Error code indicating that a field was set to an inconsistent type. - /// - IncorrectType = 111, - - /// - /// Error code indicating an invalid channel name. A channel name is either - /// an empty string (the broadcast channel) or contains only a-zA-Z0-9_ - /// characters and starts with a letter. - /// - InvalidChannelName = 112, - - /// - /// Error code indicating that push is misconfigured. - /// - PushMisconfigured = 115, - - /// - /// Error code indicating that the object is too large. - /// - ObjectTooLarge = 116, - - /// - /// Error code indicating that the operation isn't allowed for clients. - /// - OperationForbidden = 119, - - /// - /// Error code indicating the result was not found in the cache. - /// - CacheMiss = 120, - - /// - /// Error code indicating that an invalid key was used in a nested - /// JSONObject. - /// - InvalidNestedKey = 121, - - /// - /// Error code indicating that an invalid filename was used for AVFile. - /// A valid file name contains only a-zA-Z0-9_. characters and is between 1 - /// and 128 characters. - /// - InvalidFileName = 122, - - /// - /// Error code indicating an invalid ACL was provided. - /// - InvalidACL = 123, - - /// - /// Error code indicating that the request timed out on the server. Typically - /// this indicates that the request is too expensive to run. - /// - Timeout = 124, - - /// - /// Error code indicating that the email address was invalid. - /// - InvalidEmailAddress = 125, - - /// - /// Error code indicating that a unique field was given a value that is - /// already taken. - /// - DuplicateValue = 137, - - /// - /// Error code indicating that a role's name is invalid. - /// - InvalidRoleName = 139, - - /// - /// Error code indicating that an application quota was exceeded. Upgrade to - /// resolve. - /// - ExceededQuota = 140, - - /// - /// Error code indicating that a Cloud Code script failed. - /// - ScriptFailed = 141, - - /// - /// Error code indicating that a Cloud Code validation failed. - /// - ValidationFailed = 142, - - /// - /// Error code indicating that deleting a file failed. - /// - FileDeleteFailed = 153, - - /// - /// Error code indicating that the application has exceeded its request limit. - /// - RequestLimitExceeded = 155, - - /// - /// Error code indicating that the provided event name is invalid. - /// - InvalidEventName = 160, - - /// - /// Error code indicating that the username is missing or empty. - /// - UsernameMissing = 200, - - /// - /// Error code indicating that the password is missing or empty. - /// - PasswordMissing = 201, - - /// - /// Error code indicating that the username has already been taken. - /// - UsernameTaken = 202, - - /// - /// Error code indicating that the email has already been taken. - /// - EmailTaken = 203, - - /// - /// Error code indicating that the email is missing, but must be specified. - /// - EmailMissing = 204, - - /// - /// Error code indicating that a user with the specified email was not found. - /// - EmailNotFound = 205, - - /// - /// Error code indicating that a user object without a valid session could - /// not be altered. - /// - SessionMissing = 206, - - /// - /// Error code indicating that a user can only be created through signup. - /// - MustCreateUserThroughSignup = 207, - - /// - /// Error code indicating that an an account being linked is already linked - /// to another user. - /// - AccountAlreadyLinked = 208, - - /// - /// Error code indicating that the current session token is invalid. - /// - InvalidSessionToken = 209, - - /// - /// Error code indicating that a user cannot be linked to an account because - /// that account's id could not be found. - /// - LinkedIdMissing = 250, - - /// - /// Error code indicating that a user with a linked (e.g. Facebook) account - /// has an invalid session. - /// - InvalidLinkedSession = 251, - - /// - /// Error code indicating that a service being linked (e.g. Facebook or - /// Twitter) is unsupported. - /// - UnsupportedService = 252, - - /// - /// 手机号不合法 - /// - MobilePhoneInvalid = 253 - } - - internal AVException(ErrorCode code, string message, Exception cause = null) - : base(message, cause) - { - this.Code = code; - } - - /// - /// The LeanCloud error code associated with the exception. - /// - public ErrorCode Code { get; private set; } - } -} diff --git a/Storage/Source/Public/AVExtensions.cs b/Storage/Source/Public/AVExtensions.cs deleted file mode 100644 index 973ff5c..0000000 --- a/Storage/Source/Public/AVExtensions.cs +++ /dev/null @@ -1,160 +0,0 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using System.Linq; -using LeanCloud.Storage.Internal; - -namespace LeanCloud -{ - /// - /// Provides convenience extension methods for working with collections - /// of AVObjects so that you can easily save and fetch them in batches. - /// - public static class AVExtensions - { - /// - /// Saves all of the AVObjects in the enumeration. Equivalent to - /// calling . - /// - /// The objects to save. - public static Task SaveAllAsync(this IEnumerable objects) where T : AVObject - { - return AVObject.SaveAllAsync(objects); - } - - /// - /// Saves all of the AVObjects in the enumeration. Equivalent to - /// calling - /// . - /// - /// The objects to save. - /// The cancellation token. - public static Task SaveAllAsync( - this IEnumerable objects, CancellationToken cancellationToken) where T : AVObject - { - return AVObject.SaveAllAsync(objects, cancellationToken); - } - - /// - /// Fetches all of the objects in the enumeration. Equivalent to - /// calling . - /// - /// The objects to save. - public static Task> FetchAllAsync(this IEnumerable objects) - where T : AVObject - { - return AVObject.FetchAllAsync(objects); - } - - /// - /// Fetches all of the objects in the enumeration. Equivalent to - /// calling - /// . - /// - /// The objects to fetch. - /// The cancellation token. - public static Task> FetchAllAsync( - this IEnumerable objects, CancellationToken cancellationToken) - where T : AVObject - { - return AVObject.FetchAllAsync(objects, cancellationToken); - } - - /// - /// Fetches all of the objects in the enumeration that don't already have - /// data. Equivalent to calling - /// . - /// - /// The objects to fetch. - public static Task> FetchAllIfNeededAsync( - this IEnumerable objects) - where T : AVObject - { - return AVObject.FetchAllIfNeededAsync(objects); - } - - /// - /// Fetches all of the objects in the enumeration that don't already have - /// data. Equivalent to calling - /// . - /// - /// The objects to fetch. - /// The cancellation token. - public static Task> FetchAllIfNeededAsync( - this IEnumerable objects, CancellationToken cancellationToken) - where T : AVObject - { - return AVObject.FetchAllIfNeededAsync(objects, cancellationToken); - } - - /// - /// Constructs a query that is the or of the given queries. - /// - /// The type of AVObject being queried. - /// An initial query to 'or' with additional queries. - /// The list of AVQueries to 'or' together. - /// A query that is the or of the given queries. - public static AVQuery Or(this AVQuery source, params AVQuery[] queries) - where T : AVObject - { - return AVQuery.Or(queries.Concat(new[] { source })); - } - - /// - /// Fetches this object with the data from the server. - /// - public static Task FetchAsync(this T obj) where T : AVObject - { - return obj.FetchAsyncInternal(CancellationToken.None).OnSuccess(t => (T)t.Result); - } - - /// - /// Fetches this object with the data from the server. - /// - /// The AVObject to fetch. - /// The cancellation token. - public static Task FetchAsync(this T obj, CancellationToken cancellationToken) - where T : AVObject - { - return FetchAsync(obj, null, cancellationToken); - } - public static Task FetchAsync(this T obj, IEnumerable includeKeys) where T : AVObject - { - return FetchAsync(obj, includeKeys, CancellationToken.None).OnSuccess(t => (T)t.Result); - } - public static Task FetchAsync(this T obj, IEnumerable includeKeys, CancellationToken cancellationToken) - where T : AVObject - { - var queryString = new Dictionary(); - if (includeKeys != null) - { - - var encode = string.Join(",", includeKeys.ToArray()); - queryString.Add("include", encode); - } - return obj.FetchAsyncInternal(queryString, cancellationToken).OnSuccess(t => (T)t.Result); - } - - /// - /// If this AVObject has not been fetched (i.e. returns - /// false), fetches this object with the data from the server. - /// - /// The AVObject to fetch. - public static Task FetchIfNeededAsync(this T obj) where T : AVObject - { - return obj.FetchIfNeededAsyncInternal(CancellationToken.None).OnSuccess(t => (T)t.Result); - } - - /// - /// If this AVObject has not been fetched (i.e. returns - /// false), fetches this object with the data from the server. - /// - /// The AVObject to fetch. - /// The cancellation token. - public static Task FetchIfNeededAsync(this T obj, CancellationToken cancellationToken) - where T : AVObject - { - return obj.FetchIfNeededAsyncInternal(cancellationToken).OnSuccess(t => (T)t.Result); - } - } -} diff --git a/Storage/Source/Public/AVFieldNameAttribute.cs b/Storage/Source/Public/AVFieldNameAttribute.cs deleted file mode 100644 index 8ee91a7..0000000 --- a/Storage/Source/Public/AVFieldNameAttribute.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud -{ - /// - /// Specifies a field name for a property on a AVObject subclass. - /// - [AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)] - public sealed class AVFieldNameAttribute : Attribute - { - /// - /// Constructs a new AVFieldName attribute. - /// - /// The name of the field on the AVObject that the - /// property represents. - public AVFieldNameAttribute(string fieldName) - { - FieldName = fieldName; - } - - /// - /// Gets the name of the field represented by this property. - /// - public string FieldName { get; private set; } - } -} diff --git a/Storage/Source/Public/AVFile.cs b/Storage/Source/Public/AVFile.cs deleted file mode 100644 index 5e9edb7..0000000 --- a/Storage/Source/Public/AVFile.cs +++ /dev/null @@ -1,728 +0,0 @@ -using LeanCloud.Storage.Internal; -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud -{ - /// - /// AVFile is a local representation of a file that is saved to the LeanCloud. - /// - /// - /// The workflow is to construct a with data and a filename, - /// then save it and set it as a field on a AVObject: - /// - /// - /// var file = new AVFile("hello.txt", - /// new MemoryStream(Encoding.UTF8.GetBytes("hello"))); - /// await file.SaveAsync(); - /// var obj = new AVObject("TestObject"); - /// obj["file"] = file; - /// await obj.SaveAsync(); - /// - /// - public partial class AVFile : IJsonConvertible - { - internal static int objectCounter = 0; - internal static readonly object Mutex = new object(); - private FileState state; - private readonly Stream dataStream; - private readonly TaskQueue taskQueue = new TaskQueue(); - - #region Constructor - /// - /// 通过文件名,数据流,文件类型,文件源信息构建一个 AVFile - /// - /// 文件名 - /// 数据流 - /// 文件类型 - /// 文件源信息 - public AVFile(string name, Stream data, string mimeType = null, IDictionary metaData = null) - { - mimeType = mimeType == null ? GetMIMEType(name) : mimeType; - state = new FileState - { - Name = name, - MimeType = mimeType, - MetaData = metaData - }; - this.dataStream = data; - lock (Mutex) - { - objectCounter++; - state.counter = objectCounter; - } - } - - /// - /// 根据文件名,文件 Byte 数组,以及文件类型构建 AVFile - /// - /// 文件名 - /// 文件 Byte 数组 - /// 文件类型 - public AVFile(string name, byte[] data, string mimeType = null) - : this(name, new MemoryStream(data), mimeType) { } - - /// - /// 根据文件名,文件流数据,文件类型构建 AVFile - /// - /// 文件名 - /// 文件流数据 - /// 文件类型 - public AVFile(string name, Stream data, string mimeType = null) - : this(name, data, mimeType, new Dictionary()) - { - } - - /// - /// 根据 byte 数组以及文件名创建文件 - /// - /// 文件名 - /// 文件的 byte[] 数据 - public AVFile(string name, byte[] data) - : this(name, new MemoryStream(data), new Dictionary()) - { - - } - - /// - /// 根据文件名,数据 byte[] 数组以及元数据创建文件 - /// - /// 文件名 - /// 文件的 byte[] 数据 - /// 元数据 - public AVFile(string name, byte[] data, IDictionary metaData) - : this(name, new MemoryStream(data), metaData) - { - - } - - /// - /// 根据文件名,数据流以及元数据创建文件 - /// - /// 文件名 - /// 文件的数据流 - /// 元数据 - public AVFile(string name, Stream data, IDictionary metaData) - : this(name, data, GetMIMEType(name), metaData) - { - } - - /// - /// 根据文件名,数据流以及元数据创建文件 - /// - /// 文件名 - /// 文件的数据流 - public AVFile(string name, Stream data) - : this(name, data, new Dictionary()) - { - - } - - #region created by url or uri - /// - /// 根据文件名,Uri,文件类型以及文件源信息 - /// - /// 文件名 - /// 文件Uri - /// 文件类型 - /// 文件源信息 - public AVFile(string name, Uri uri, string mimeType = null, IDictionary metaData = null) - { - mimeType = mimeType == null ? GetMIMEType(name) : mimeType; - state = new FileState - { - Name = name, - Url = uri, - MetaData = metaData, - MimeType = mimeType - }; - lock (Mutex) - { - objectCounter++; - state.counter = objectCounter; - } - this.isExternal = true; - } - - /// - /// 根据文件名,文件 Url,文件类型,文件源信息构建 AVFile - /// - /// 文件名 - /// 文件 Url - /// 文件类型 - /// 文件源信息 - public AVFile(string name, string url, string mimeType = null, IDictionary metaData = null) - : this(name, new Uri(url), mimeType, metaData) - { - - } - - /// - /// 根据文件名,文件 Url以及文件的源信息构建 AVFile - /// - /// 文件名 - /// 文件 Url - /// 文件源信息 - public AVFile(string name, string url, IDictionary metaData) - : this(name, url, null, metaData) - { - } - - /// - /// 根据文件名,文件 Uri,以及文件类型构建 AVFile - /// - /// 文件名 - /// 文件 Uri - /// 文件类型 - public AVFile(string name, Uri uri, string mimeType = null) - : this(name, uri, mimeType, new Dictionary()) - { - - } - - /// - /// 根据文件名以及文件 Uri 构建 AVFile - /// - /// 文件名 - /// 文件 Uri - public AVFile(string name, Uri uri) - : this(name, uri, null, new Dictionary()) - { - - } - /// - /// 根据文件名和 Url 创建文件 - /// - /// 文件名 - /// 文件的 Url - public AVFile(string name, string url) - : this(name, new Uri(url)) - { - } - - internal AVFile(FileState filestate) - { - this.state = filestate; - } - internal AVFile(string objectId) - : this(new FileState() - { - ObjectId = objectId - }) - { - - } - #endregion - - #endregion - - #region Properties - - /// - /// Gets whether the file still needs to be saved. - /// - public bool IsDirty - { - get - { - return state.Url == null; - } - } - - /// - /// Gets the name of the file. Before save is called, this is the filename given by - /// the user. After save is called, that name gets prefixed with a unique identifier. - /// - [AVFieldName("name")] - public string Name - { - get - { - return state.Name; - } - } - - /// - /// Gets the MIME type of the file. This is either passed in to the constructor or - /// inferred from the file extension. "unknown/unknown" will be used if neither is - /// available. - /// - public string MimeType - { - get - { - return state.MimeType; - } - } - - /// - /// Gets the url of the file. It is only available after you save the file or after - /// you get the file from a . - /// - [AVFieldName("url")] - public Uri Url - { - get - { - return state.Url; - } - } - - internal static IAVFileController FileController - { - get - { - return AVPlugins.Instance.FileController; - } - } - - #endregion - - IDictionary IJsonConvertible.ToJSON() - { - if (this.IsDirty) - { - throw new InvalidOperationException( - "AVFile must be saved before it can be serialized."); - } - return new Dictionary { - {"__type", "File"}, - { "id", ObjectId }, - {"name", Name}, - {"url", Url.AbsoluteUri} - }; - } - - #region Save - - /// - /// Saves the file to the LeanCloud cloud. - /// - public Task SaveAsync() - { - return SaveAsync(null, CancellationToken.None); - } - - /// - /// Saves the file to the LeanCloud cloud. - /// - /// The cancellation token. - public Task SaveAsync(CancellationToken cancellationToken) - { - return SaveAsync(null, cancellationToken); - } - - /// - /// Saves the file to the LeanCloud cloud. - /// - /// The progress callback. - public Task SaveAsync(IProgress progress) - { - return SaveAsync(progress, CancellationToken.None); - } - - /// - /// Saves the file to the LeanCloud cloud. - /// - /// The progress callback. - /// The cancellation token. - public Task SaveAsync(IProgress progress, - CancellationToken cancellationToken) - { - if (this.isExternal) - return this.SaveExternal(); - - return taskQueue.Enqueue( - toAwait => FileController.SaveAsync(state, dataStream, AVUser.CurrentSessionToken, progress, cancellationToken), cancellationToken) - .OnSuccess(t => - { - state = t.Result; - }); - } - - internal Task SaveExternal() - { - Dictionary strs = new Dictionary() - { - { "url", this.Url.ToString() }, - { "name",this.Name }, - { "mime_type",this.MimeType}, - { "metaData",this.MetaData} - }; - AVCommand cmd = null; - - if (!string.IsNullOrEmpty(this.ObjectId)) - { - cmd = new AVCommand("files/" + this.ObjectId, - method: "PUT", sessionToken: AVUser.CurrentSessionToken, data: strs); - } - else - { - cmd = new AVCommand("files", method: "POST", sessionToken: AVUser.CurrentSessionToken, data: strs); - } - - return AVPlugins.Instance.CommandRunner.RunCommandAsync(cmd).ContinueWith(t => - { - var result = t.Result.Item2; - this.state.ObjectId = result["objectId"].ToString(); - return AVClient.IsSuccessStatusCode(t.Result.Item1); - }); - } - - #endregion - - #region Compatible - private readonly static Dictionary MIMETypesDictionary; - private object mutex = new object(); - private bool isExternal; - /// - /// 文件在 LeanCloud 的唯一Id 标识 - /// - public string ObjectId - { - get - { - string str; - lock (this.mutex) - { - str = state.ObjectId; - } - return str; - } - } - - /// - /// 文件的元数据。 - /// - public IDictionary MetaData - { - get - { - return state.MetaData; - } - } - - /// - /// 文件是否为外链文件。 - /// - /// - /// - public bool IsExternal - { - get - { - return isExternal; - } - } - - static AVFile() - { - Dictionary strs = new Dictionary() - { - { "ai", "application/postscript" }, - { "aif", "audio/x-aiff" }, - { "aifc", "audio/x-aiff" }, - { "aiff", "audio/x-aiff" }, - { "asc", "text/plain" }, - { "atom", "application/atom+xml" }, - { "au", "audio/basic" }, - { "avi", "video/x-msvideo" }, - { "bcpio", "application/x-bcpio" }, - { "bin", "application/octet-stream" }, - { "bmp", "image/bmp" }, - { "cdf", "application/x-netcdf" }, - { "cgm", "image/cgm" }, - { "class", "application/octet-stream" }, - { "cpio", "application/x-cpio" }, - { "cpt", "application/mac-compactpro" }, - { "csh", "application/x-csh" }, - { "css", "text/css" }, - { "dcr", "application/x-director" }, - { "dif", "video/x-dv" }, - { "dir", "application/x-director" }, - { "djv", "image/vnd.djvu" }, - { "djvu", "image/vnd.djvu" }, - { "dll", "application/octet-stream" }, - { "dmg", "application/octet-stream" }, - { "dms", "application/octet-stream" }, - { "doc", "application/msword" }, - { "docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }, - { "dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template" }, - { "docm", "application/vnd.ms-word.document.macroEnabled.12" }, - { "dotm", "application/vnd.ms-word.template.macroEnabled.12" }, - { "dtd", "application/xml-dtd" }, - { "dv", "video/x-dv" }, - { "dvi", "application/x-dvi" }, - { "dxr", "application/x-director" }, - { "eps", "application/postscript" }, - { "etx", "text/x-setext" }, - { "exe", "application/octet-stream" }, - { "ez", "application/andrew-inset" }, - { "gif", "image/gif" }, - { "gram", "application/srgs" }, - { "grxml", "application/srgs+xml" }, - { "gtar", "application/x-gtar" }, - { "hdf", "application/x-hdf" }, - { "hqx", "application/mac-binhex40" }, - { "htm", "text/html" }, - { "html", "text/html" }, - { "ice", "x-conference/x-cooltalk" }, - { "ico", "image/x-icon" }, - { "ics", "text/calendar" }, - { "ief", "image/ief" }, - { "ifb", "text/calendar" }, - { "iges", "model/iges" }, - { "igs", "model/iges" }, - { "jnlp", "application/x-java-jnlp-file" }, - { "jp2", "image/jp2" }, - { "jpe", "image/jpeg" }, - { "jpeg", "image/jpeg" }, - { "jpg", "image/jpeg" }, - { "js", "application/x-javascript" }, - { "kar", "audio/midi" }, - { "latex", "application/x-latex" }, - { "lha", "application/octet-stream" }, - { "lzh", "application/octet-stream" }, - { "m3u", "audio/x-mpegurl" }, - { "m4a", "audio/mp4a-latm" }, - { "m4b", "audio/mp4a-latm" }, - { "m4p", "audio/mp4a-latm" }, - { "m4u", "video/vnd.mpegurl" }, - { "m4v", "video/x-m4v" }, - { "mac", "image/x-macpaint" }, - { "man", "application/x-troff-man" }, - { "mathml", "application/mathml+xml" }, - { "me", "application/x-troff-me" }, - { "mesh", "model/mesh" }, - { "mid", "audio/midi" }, - { "midi", "audio/midi" }, - { "mif", "application/vnd.mif" }, - { "mov", "video/quicktime" }, - { "movie", "video/x-sgi-movie" }, - { "mp2", "audio/mpeg" }, - { "mp3", "audio/mpeg" }, - { "mp4", "video/mp4" }, - { "mpe", "video/mpeg" }, - { "mpeg", "video/mpeg" }, - { "mpg", "video/mpeg" }, - { "mpga", "audio/mpeg" }, - { "ms", "application/x-troff-ms" }, - { "msh", "model/mesh" }, - { "mxu", "video/vnd.mpegurl" }, - { "nc", "application/x-netcdf" }, - { "oda", "application/oda" }, - { "ogg", "application/ogg" }, - { "pbm", "image/x-portable-bitmap" }, - { "pct", "image/pict" }, - { "pdb", "chemical/x-pdb" }, - { "pdf", "application/pdf" }, - { "pgm", "image/x-portable-graymap" }, - { "pgn", "application/x-chess-pgn" }, - { "pic", "image/pict" }, - { "pict", "image/pict" }, - { "png", "image/png" }, - { "pnm", "image/x-portable-anymap" }, - { "pnt", "image/x-macpaint" }, - { "pntg", "image/x-macpaint" }, - { "ppm", "image/x-portable-pixmap" }, - { "ppt", "application/vnd.ms-powerpoint" }, - { "pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation" }, - { "potx", "application/vnd.openxmlformats-officedocument.presentationml.template" }, - { "ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow" }, - { "ppam", "application/vnd.ms-powerpoint.addin.macroEnabled.12" }, - { "pptm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12" }, - { "potm", "application/vnd.ms-powerpoint.template.macroEnabled.12" }, - { "ppsm", "application/vnd.ms-powerpoint.slideshow.macroEnabled.12" }, - { "ps", "application/postscript" }, - { "qt", "video/quicktime" }, - { "qti", "image/x-quicktime" }, - { "qtif", "image/x-quicktime" }, - { "ra", "audio/x-pn-realaudio" }, - { "ram", "audio/x-pn-realaudio" }, - { "ras", "image/x-cmu-raster" }, - { "rdf", "application/rdf+xml" }, - { "rgb", "image/x-rgb" }, - { "rm", "application/vnd.rn-realmedia" }, - { "roff", "application/x-troff" }, - { "rtf", "text/rtf" }, - { "rtx", "text/richtext" }, - { "sgm", "text/sgml" }, - { "sgml", "text/sgml" }, - { "sh", "application/x-sh" }, - { "shar", "application/x-shar" }, - { "silo", "model/mesh" }, - { "sit", "application/x-stuffit" }, - { "skd", "application/x-koan" }, - { "skm", "application/x-koan" }, - { "skp", "application/x-koan" }, - { "skt", "application/x-koan" }, - { "smi", "application/smil" }, - { "smil", "application/smil" }, - { "snd", "audio/basic" }, - { "so", "application/octet-stream" }, - { "spl", "application/x-futuresplash" }, - { "src", "application/x-wais-Source" }, - { "sv4cpio", "application/x-sv4cpio" }, - { "sv4crc", "application/x-sv4crc" }, - { "svg", "image/svg+xml" }, - { "swf", "application/x-shockwave-flash" }, - { "t", "application/x-troff" }, - { "tar", "application/x-tar" }, - { "tcl", "application/x-tcl" }, - { "tex", "application/x-tex" }, - { "texi", "application/x-texinfo" }, - { "texinfo", "application/x-texinfo" }, - { "tif", "image/tiff" }, - { "tiff", "image/tiff" }, - { "tr", "application/x-troff" }, - { "tsv", "text/tab-separated-values" }, - { "txt", "text/plain" }, - { "ustar", "application/x-ustar" }, - { "vcd", "application/x-cdlink" }, - { "vrml", "model/vrml" }, - { "vxml", "application/voicexml+xml" }, - { "wav", "audio/x-wav" }, - { "wbmp", "image/vnd.wap.wbmp" }, - { "wbmxl", "application/vnd.wap.wbxml" }, - { "wml", "text/vnd.wap.wml" }, - { "wmlc", "application/vnd.wap.wmlc" }, - { "wmls", "text/vnd.wap.wmlscript" }, - { "wmlsc", "application/vnd.wap.wmlscriptc" }, - { "wrl", "model/vrml" }, - { "xbm", "image/x-xbitmap" }, - { "xht", "application/xhtml+xml" }, - { "xhtml", "application/xhtml+xml" }, - { "xls", "application/vnd.ms-excel" }, - { "xml", "application/xml" }, - { "xpm", "image/x-xpixmap" }, - { "xsl", "application/xml" }, - { "xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" }, - { "xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template" }, - { "xlsm", "application/vnd.ms-excel.sheet.macroEnabled.12" }, - { "xltm", "application/vnd.ms-excel.template.macroEnabled.12" }, - { "xlam", "application/vnd.ms-excel.addin.macroEnabled.12" }, - { "xlsb", "application/vnd.ms-excel.sheet.binary.macroEnabled.12" }, - { "xslt", "application/xslt+xml" }, - { "xul", "application/vnd.mozilla.xul+xml" }, - { "xwd", "image/x-xwindowdump" }, - { "xyz", "chemical/x-xyz" }, - { "zip", "application/zip" }, - }; - AVFile.MIMETypesDictionary = strs; - } - internal static string GetMIMEType(string fileName) - { - try - { - string str = Path.GetExtension(fileName).Remove(0, 1); - if (!AVFile.MIMETypesDictionary.ContainsKey(str)) - { - return "unknown/unknown"; - } - return AVFile.MIMETypesDictionary[str]; - } - catch - { - return "unknown/unknown"; - } - } - - /// - /// 根据 ObjectId 获取文件 - /// - /// 获取之后并没有实际执行下载,只是加载了文件的元信息以及物理地址(Url) - /// - public static Task GetFileWithObjectIdAsync(string objectId, CancellationToken cancellationToken) - { - string currentSessionToken = AVUser.CurrentSessionToken; - return FileController.GetAsync(objectId, currentSessionToken, cancellationToken).OnSuccess(_ => - { - var filestate = _.Result; - return new AVFile(filestate); - }); - } - - public static AVFile CreateWithoutData(string objectId) - { - return new AVFile(objectId); - } - - public static AVFile CreateWithState(FileState state) - { - return new AVFile(state); - } - - public static AVFile CreateWithData(string objectId,string name, string url,IDictionary metaData) - { - var fileState = new FileState(); - fileState.Name = name; - fileState.ObjectId = objectId; - fileState.Url = new Uri(url); - fileState.MetaData = metaData; - return CreateWithState(fileState); - } - /// - /// 根据 ObjectId 获取文件 - /// - /// 获取之后并没有实际执行下载,只是加载了文件的元信息以及物理地址(Url) - /// - public static Task GetFileWithObjectIdAsync(string objectId) - { - return GetFileWithObjectIdAsync(objectId, CancellationToken.None); - } - - internal void MergeFromJSON(IDictionary jsonData) - { - lock (this.mutex) - { - state.ObjectId = jsonData["objectId"] as string; - state.Url = new Uri(jsonData["url"] as string, UriKind.Absolute); - if (jsonData.ContainsKey("name")) - { - state.Name = jsonData["name"] as string; - } - if (jsonData.ContainsKey("metaData")) - { - state.MetaData = jsonData["metaData"] as Dictionary; - } - - } - } - - /// - /// 删除文件 - /// - /// Task - public Task DeleteAsync() - { - return DeleteAsync(CancellationToken.None); - } - internal Task DeleteAsync(CancellationToken cancellationToken) - { - return taskQueue.Enqueue(toAwait => DeleteAsync(toAwait, cancellationToken), - cancellationToken); - - } - internal Task DeleteAsync(Task toAwait, CancellationToken cancellationToken) - { - if (ObjectId == null) - { - return Task.FromResult(0); - } - - string sessionToken = AVUser.CurrentSessionToken; - - return toAwait.OnSuccess(_ => - { - return FileController.DeleteAsync(state, sessionToken, cancellationToken); - }).Unwrap().OnSuccess(_ => { }); - } - #endregion - } - - -} diff --git a/Storage/Source/Public/AVGeoDistance.cs b/Storage/Source/Public/AVGeoDistance.cs deleted file mode 100644 index e761658..0000000 --- a/Storage/Source/Public/AVGeoDistance.cs +++ /dev/null @@ -1,78 +0,0 @@ -namespace LeanCloud -{ - /// - /// Represents a distance between two AVGeoPoints. - /// - public struct AVGeoDistance - { - private const double EarthMeanRadiusKilometers = 6371.0; - private const double EarthMeanRadiusMiles = 3958.8; - - /// - /// Creates a AVGeoDistance. - /// - /// The distance in radians. - public AVGeoDistance(double radians) - : this() - { - Radians = radians; - } - - /// - /// Gets the distance in radians. - /// - public double Radians { get; private set; } - - /// - /// Gets the distance in miles. - /// - public double Miles - { - get - { - return Radians * EarthMeanRadiusMiles; - } - } - - /// - /// Gets the distance in kilometers. - /// - public double Kilometers - { - get - { - return Radians * EarthMeanRadiusKilometers; - } - } - - /// - /// Gets a AVGeoDistance from a number of miles. - /// - /// The number of miles. - /// A AVGeoDistance for the given number of miles. - public static AVGeoDistance FromMiles(double miles) - { - return new AVGeoDistance(miles / EarthMeanRadiusMiles); - } - - /// - /// Gets a AVGeoDistance from a number of kilometers. - /// - /// The number of kilometers. - /// A AVGeoDistance for the given number of kilometers. - public static AVGeoDistance FromKilometers(double kilometers) - { - return new AVGeoDistance(kilometers / EarthMeanRadiusKilometers); - } - - /// - /// Gets a AVGeoDistance from a number of radians. - /// - /// The number of radians. - /// A AVGeoDistance for the given number of radians. - public static AVGeoDistance FromRadians(double radians) - { - return new AVGeoDistance(radians); - } - } -} diff --git a/Storage/Source/Public/AVGeoPoint.cs b/Storage/Source/Public/AVGeoPoint.cs deleted file mode 100644 index 7ca0f57..0000000 --- a/Storage/Source/Public/AVGeoPoint.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; -using System.Collections.Generic; -using LeanCloud.Storage.Internal; - -namespace LeanCloud -{ - /// - /// AVGeoPoint represents a latitude / longitude point that may be associated - /// with a key in a AVObject or used as a reference point for geo queries. - /// This allows proximity-based queries on the key. - /// - /// Only one key in a class may contain a GeoPoint. - /// - public struct AVGeoPoint : IJsonConvertible - { - /// - /// Constructs a AVGeoPoint with the specified latitude and longitude. - /// - /// The point's latitude. - /// The point's longitude. - public AVGeoPoint(double latitude, double longitude) - : this() - { - Latitude = latitude; - Longitude = longitude; - } - - private double latitude; - /// - /// Gets or sets the latitude of the GeoPoint. Valid range is [-90, 90]. - /// Extremes should not be used. - /// - public double Latitude - { - get - { - return latitude; - } - set - { - if (value > 90 || value < -90) - { - throw new ArgumentOutOfRangeException("value", - "Latitude must be within the range [-90, 90]"); - } - latitude = value; - } - } - - private double longitude; - /// - /// Gets or sets the longitude. Valid range is [-180, 180]. - /// Extremes should not be used. - /// - public double Longitude - { - get - { - return longitude; - } - set - { - if (value > 180 || value < -180) - { - throw new ArgumentOutOfRangeException("value", - "Longitude must be within the range [-180, 180]"); - } - longitude = value; - } - } - - /// - /// Get the distance in radians between this point and another GeoPoint. This is the smallest angular - /// distance between the two points. - /// - /// GeoPoint describing the other point being measured against. - /// The distance in between the two points. - public AVGeoDistance DistanceTo(AVGeoPoint point) - { - double d2r = Math.PI / 180; // radian conversion factor - double lat1rad = Latitude * d2r; - double long1rad = longitude * d2r; - double lat2rad = point.Latitude * d2r; - double long2rad = point.Longitude * d2r; - double deltaLat = lat1rad - lat2rad; - double deltaLong = long1rad - long2rad; - double sinDeltaLatDiv2 = Math.Sin(deltaLat / 2); - double sinDeltaLongDiv2 = Math.Sin(deltaLong / 2); - // Square of half the straight line chord distance between both points. - // [0.0, 1.0] - double a = sinDeltaLatDiv2 * sinDeltaLatDiv2 + - Math.Cos(lat1rad) * Math.Cos(lat2rad) * sinDeltaLongDiv2 * sinDeltaLongDiv2; - a = Math.Min(1.0, a); - return new AVGeoDistance(2 * Math.Asin(Math.Sqrt(a))); - } - - IDictionary IJsonConvertible.ToJSON() - { - return new Dictionary - { - { "__type", "GeoPoint" }, - { "latitude", Latitude }, - { "longitude", Longitude } - }; - } - } -} diff --git a/Storage/Source/Public/AVObject.cs b/Storage/Source/Public/AVObject.cs deleted file mode 100644 index 0f9cde7..0000000 --- a/Storage/Source/Public/AVObject.cs +++ /dev/null @@ -1,2099 +0,0 @@ -using LeanCloud.Storage.Internal; -using LeanCloud.Utilities; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Net; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; -using System.Collections; - -namespace LeanCloud -{ - /// - /// The AVObject is a local representation of data that can be saved and - /// retrieved from the LeanCloud cloud. - /// - /// - /// The basic workflow for creating new data is to construct a new AVObject, - /// use the indexer to fill it with data, and then use SaveAsync() to persist to the - /// database. - /// - /// - /// The basic workflow for accessing existing data is to use a AVQuery - /// to specify which existing data to retrieve. - /// - /// - public class AVObject : IEnumerable>, INotifyPropertyChanged, INotifyPropertyUpdated, INotifyCollectionPropertyUpdated, IAVObject - { - private static readonly string AutoClassName = "_Automatic"; - -#if UNITY - private static readonly bool isCompiledByIL2CPP = AppDomain.CurrentDomain.FriendlyName.Equals("IL2CPP Root Domain"); -#else - private static readonly bool isCompiledByIL2CPP = false; -#endif - - - internal readonly object mutex = new object(); - - private readonly LinkedList> operationSetQueue = - new LinkedList>(); - private readonly IDictionary estimatedData = new Dictionary(); - - private static readonly ThreadLocal isCreatingPointer = new ThreadLocal(() => false); - - private bool hasBeenFetched; - private bool dirty; - internal TaskQueue taskQueue = new TaskQueue(); - - private IObjectState state; - internal void MutateState(Action func) - { - lock (mutex) - { - state = state.MutatedClone(func); - - // Refresh the estimated data. - RebuildEstimatedData(); - } - } - - public IObjectState State - { - get - { - return state; - } - } - - internal static IAVObjectController ObjectController - { - get - { - return AVPlugins.Instance.ObjectController; - } - } - - internal static IObjectSubclassingController SubclassingController - { - get - { - return AVPlugins.Instance.SubclassingController; - } - } - - public static string GetSubClassName() - { - return SubclassingController.GetClassName(typeof(TAVObject)); - } - - #region AVObject Creation - - /// - /// Constructor for use in AVObject subclasses. Subclasses must specify a AVClassName attribute. - /// - protected AVObject() - : this(AutoClassName) - { - } - - /// - /// Constructs a new AVObject with no data in it. A AVObject constructed in this way will - /// not have an ObjectId and will not persist to the database until - /// is called. - /// - /// - /// Class names must be alphanumerical plus underscore, and start with a letter. It is recommended - /// to name classes in CamelCaseLikeThis. - /// - /// The className for this AVObject. - public AVObject(string className) - { - // We use a ThreadLocal rather than passing a parameter so that createWithoutData can do the - // right thing with subclasses. It's ugly and terrible, but it does provide the development - // experience we generally want, so... yeah. Sorry to whomever has to deal with this in the - // future. I pinky-swear we won't make a habit of this -- you believe me, don't you? - var isPointer = isCreatingPointer.Value; - isCreatingPointer.Value = false; - - if (className == null) - { - throw new ArgumentException("You must specify a LeanCloud class name when creating a new AVObject."); - } - if (AutoClassName.Equals(className)) - { - className = SubclassingController.GetClassName(GetType()); - } - // If this is supposed to be created by a factory but wasn't, throw an exception - if (!SubclassingController.IsTypeValid(className, GetType())) - { - throw new ArgumentException( - "You must create this type of AVObject using AVObject.Create() or the proper subclass."); - } - state = new MutableObjectState - { - ClassName = className - }; - OnPropertyChanged("ClassName"); - - operationSetQueue.AddLast(new Dictionary()); - if (!isPointer) - { - hasBeenFetched = true; - IsDirty = true; - SetDefaultValues(); - } - else - { - IsDirty = false; - hasBeenFetched = false; - } - } - - /// - /// Creates a new AVObject based upon a class name. If the class name is a special type (e.g. - /// for ), then the appropriate type of AVObject is returned. - /// - /// The class of object to create. - /// A new AVObject for the given class name. - public static AVObject Create(string className) - { - return SubclassingController.Instantiate(className); - } - - /// - /// Creates a reference to an existing AVObject for use in creating associations between - /// AVObjects. Calling on this object will return - /// false until has been called. - /// No network request will be made. - /// - /// The object's class. - /// The object id for the referenced object. - /// A AVObject without data. - public static AVObject CreateWithoutData(string className, string objectId) - { - isCreatingPointer.Value = true; - try - { - var result = SubclassingController.Instantiate(className); - result.ObjectId = objectId; - result.IsDirty = false; - if (result.IsDirty) - { - throw new InvalidOperationException( - "A AVObject subclass default constructor must not make changes to the object that cause it to be dirty."); - } - return result; - } - finally - { - isCreatingPointer.Value = false; - } - } - - /// - /// Creates a new AVObject based upon a given subclass type. - /// - /// A new AVObject for the given class name. - public static T Create() where T : AVObject - { - return (T)SubclassingController.Instantiate(SubclassingController.GetClassName(typeof(T))); - } - - /// - /// Creates a reference to an existing AVObject for use in creating associations between - /// AVObjects. Calling on this object will return - /// false until has been called. - /// No network request will be made. - /// - /// The object id for the referenced object. - /// A AVObject without data. - public static T CreateWithoutData(string objectId) where T : AVObject - { - return (T)CreateWithoutData(SubclassingController.GetClassName(typeof(T)), objectId); - } - - /// - /// restore a AVObject of subclass instance from IObjectState. - /// - /// IObjectState after encode from Dictionary. - /// The name of the subclass. - public static T FromState(IObjectState state, string defaultClassName) where T : AVObject - { - string className = state.ClassName ?? defaultClassName; - - T obj = (T)CreateWithoutData(className, state.ObjectId); - obj.HandleFetchResult(state); - - return obj; - } - - #endregion - - public static IDictionary GetPropertyMappings(string className) - { - return SubclassingController.GetPropertyMappings(className); - } - - private static string GetFieldForPropertyName(string className, string propertyName) - { - SubclassingController.GetPropertyMappings(className).TryGetValue(propertyName, out string fieldName); - return fieldName; - } - - /// - /// Sets the value of a property based upon its associated AVFieldName attribute. - /// - /// The new value. - /// The name of the property. - /// The type for the property. - protected virtual void SetProperty(T value, -#if !UNITY - [CallerMemberName] string propertyName = null -#else - string propertyName -#endif -) - { - this[GetFieldForPropertyName(ClassName, propertyName)] = value; - } - - /// - /// Gets a relation for a property based upon its associated AVFieldName attribute. - /// - /// The AVRelation for the property. - /// The name of the property. - /// The AVObject subclass type of the AVRelation. - protected AVRelation GetRelationProperty( -#if !UNITY -[CallerMemberName] string propertyName = null -#else -string propertyName -#endif -) where T : AVObject - { - return GetRelation(GetFieldForPropertyName(ClassName, propertyName)); - } - - /// - /// Gets the value of a property based upon its associated AVFieldName attribute. - /// - /// The value of the property. - /// The name of the property. - /// The return type of the property. - protected virtual T GetProperty( -#if !UNITY -[CallerMemberName] string propertyName = null -#else -string propertyName -#endif -) - { - return GetProperty(default(T), propertyName); - } - - /// - /// Gets the value of a property based upon its associated AVFieldName attribute. - /// - /// The value of the property. - /// The value to return if the property is not present on the AVObject. - /// The name of the property. - /// The return type of the property. - protected virtual T GetProperty(T defaultValue, -#if !UNITY - [CallerMemberName] string propertyName = null -#else - string propertyName -#endif -) - { - T result; - if (TryGetValue(GetFieldForPropertyName(ClassName, propertyName), out result)) - { - return result; - } - return defaultValue; - } - - /// - /// Allows subclasses to set values for non-pointer construction. - /// - internal virtual void SetDefaultValues() - { - } - - /// - /// Registers a custom subclass type with the LeanCloud SDK, enabling strong-typing of those AVObjects whenever - /// they appear. Subclasses must specify the AVClassName attribute, have a default constructor, and properties - /// backed by AVObject fields should have AVFieldName attributes supplied. - /// - /// The AVObject subclass type to register. - public static void RegisterSubclass() where T : AVObject, new() - { - SubclassingController.RegisterSubclass(typeof(T)); - } - - internal static void UnregisterSubclass() where T : AVObject, new() - { - SubclassingController.UnregisterSubclass(typeof(T)); - } - - /// - /// Clears any changes to this object made since the last call to . - /// - public void Revert() - { - lock (mutex) - { - bool wasDirty = CurrentOperations.Count > 0; - if (wasDirty) - { - CurrentOperations.Clear(); - RebuildEstimatedData(); - OnPropertyChanged("IsDirty"); - } - } - } - - internal virtual void HandleFetchResult(IObjectState serverState) - { - lock (mutex) - { - MergeFromServer(serverState); - } - } - - internal void HandleFailedSave( - IDictionary operationsBeforeSave) - { - lock (mutex) - { - var opNode = operationSetQueue.Find(operationsBeforeSave); - var nextOperations = opNode.Next.Value; - bool wasDirty = nextOperations.Count > 0; - operationSetQueue.Remove(opNode); - // Merge the data from the failed save into the next save. - foreach (var pair in operationsBeforeSave) - { - var operation1 = pair.Value; - IAVFieldOperation operation2 = null; - nextOperations.TryGetValue(pair.Key, out operation2); - if (operation2 != null) - { - operation2 = operation2.MergeWithPrevious(operation1); - } - else - { - operation2 = operation1; - } - nextOperations[pair.Key] = operation2; - } - if (!wasDirty && nextOperations == CurrentOperations && operationsBeforeSave.Count > 0) - { - OnPropertyChanged("IsDirty"); - } - } - } - - internal virtual void HandleSave(IObjectState serverState) - { - lock (mutex) - { - var operationsBeforeSave = operationSetQueue.First.Value; - operationSetQueue.RemoveFirst(); - - // Merge the data from the save and the data from the server into serverData. - //MutateState(mutableClone => - //{ - // mutableClone.Apply(operationsBeforeSave); - //}); - state = state.MutatedClone((objectState) => objectState.Apply(operationsBeforeSave)); - MergeFromServer(serverState); - } - } - - public virtual void MergeFromServer(IObjectState serverState) - { - // Make a new serverData with fetched values. - var newServerData = serverState.ToDictionary(t => t.Key, t => t.Value); - - lock (mutex) - { - // Trigger handler based on serverState - if (serverState.ObjectId != null) - { - // If the objectId is being merged in, consider this object to be fetched. - hasBeenFetched = true; - OnPropertyChanged("IsDataAvailable"); - } - - if (serverState.UpdatedAt != null) - { - OnPropertyChanged("UpdatedAt"); - } - - if (serverState.CreatedAt != null) - { - OnPropertyChanged("CreatedAt"); - } - - // We cache the fetched object because subsequent Save operation might flush - // the fetched objects into Pointers. - IDictionary fetchedObject = CollectFetchedObjects(); - - foreach (var pair in serverState) - { - var value = pair.Value; - if (value is AVObject) - { - // Resolve fetched object. - var avObject = value as AVObject; - if (fetchedObject.ContainsKey(avObject.ObjectId)) - { - value = fetchedObject[avObject.ObjectId]; - } - } - newServerData[pair.Key] = value; - } - - IsDirty = false; - serverState = serverState.MutatedClone(mutableClone => - { - mutableClone.ServerData = newServerData; - }); - MutateState(mutableClone => - { - mutableClone.Apply(serverState); - }); - } - } - - internal void MergeFromObject(AVObject other) - { - lock (mutex) - { - // If they point to the same instance, we don't need to merge - if (this == other) - { - return; - } - } - - // Clear out any changes on this object. - if (operationSetQueue.Count != 1) - { - throw new InvalidOperationException("Attempt to MergeFromObject during save."); - } - operationSetQueue.Clear(); - foreach (var operationSet in other.operationSetQueue) - { - operationSetQueue.AddLast(operationSet.ToDictionary(entry => entry.Key, - entry => entry.Value)); - } - - lock (mutex) - { - state = other.State; - } - RebuildEstimatedData(); - } - - private bool HasDirtyChildren - { - get - { - lock (mutex) - { - return FindUnsavedChildren().FirstOrDefault() != null; - } - } - } - - /// - /// Flattens dictionaries and lists into a single enumerable of all contained objects - /// that can then be queried over. - /// - /// The root of the traversal - /// Whether to traverse into AVObjects' children - /// Whether to include the root in the result - /// - internal static IEnumerable DeepTraversal( - object root, bool traverseAVObjects = false, bool yieldRoot = false) - { - var items = DeepTraversalInternal(root, - traverseAVObjects, - new HashSet(new IdentityEqualityComparer())); - if (yieldRoot) - { - return new[] { root }.Concat(items); - } - else - { - return items; - } - } - - private static IEnumerable DeepTraversalInternal( - object root, bool traverseAVObjects, ICollection seen) - { - seen.Add(root); - var itemsToVisit = isCompiledByIL2CPP ? (System.Collections.IEnumerable)null : (IEnumerable)null; - var dict = Conversion.As>(root); - if (dict != null) - { - itemsToVisit = dict.Values; - } - else - { - var list = Conversion.As>(root); - if (list != null) - { - itemsToVisit = list; - } - else if (traverseAVObjects) - { - var obj = root as AVObject; - if (obj != null) - { - itemsToVisit = obj.Keys.ToList().Select(k => obj[k]); - } - } - } - if (itemsToVisit != null) - { - foreach (var i in itemsToVisit) - { - if (!seen.Contains(i)) - { - yield return i; - var children = DeepTraversalInternal(i, traverseAVObjects, seen); - foreach (var child in children) - { - yield return child; - } - } - } - } - } - - private IEnumerable FindUnsavedChildren() - { - return DeepTraversal(estimatedData) - .OfType() - .Where(o => o.IsDirty); - } - - /// - /// Deep traversal of this object to grab a copy of any object referenced by this object. - /// These instances may have already been fetched, and we don't want to lose their data when - /// refreshing or saving. - /// - /// Map of objectId to AVObject which have been fetched. - private IDictionary CollectFetchedObjects() - { - return DeepTraversal(estimatedData) - .OfType() - .Where(o => o.ObjectId != null && o.IsDataAvailable) - .GroupBy(o => o.ObjectId) - .ToDictionary(group => group.Key, group => group.Last()); - } - - public static IDictionary ToJSONObjectForSaving( - IDictionary operations) - { - var result = new Dictionary(); - foreach (var pair in operations) - { - // AVRPCSerialize the data - var operation = pair.Value; - - result[pair.Key] = PointerOrLocalIdEncoder.Instance.Encode(operation); - } - return result; - } - - internal IDictionary EncodeForSaving(IDictionary data) - { - var result = new Dictionary(); - lock (this.mutex) - { - foreach (var key in data.Keys) - { - var value = data[key]; - result.Add(key, PointerOrLocalIdEncoder.Instance.Encode(value)); - } - } - - return result; - } - - - internal IDictionary ServerDataToJSONObjectForSerialization() - { - return PointerOrLocalIdEncoder.Instance.Encode(state.ToDictionary(t => t.Key, t => t.Value)) - as IDictionary; - } - - #region Save Object(s) - - /// - /// Pushes new operations onto the queue and returns the current set of operations. - /// - public IDictionary StartSave() - { - lock (mutex) - { - var currentOperations = CurrentOperations; - operationSetQueue.AddLast(new Dictionary()); - OnPropertyChanged("IsDirty"); - return currentOperations; - } - } - - protected virtual Task SaveAsync(Task toAwait, - CancellationToken cancellationToken) - { - IDictionary currentOperations = null; - if (!IsDirty) - { - return Task.FromResult(0); - } - - Task deepSaveTask; - string sessionToken; - lock (mutex) - { - // Get the JSON representation of the object. - currentOperations = StartSave(); - - sessionToken = AVUser.CurrentSessionToken; - - deepSaveTask = DeepSaveAsync(estimatedData, sessionToken, cancellationToken); - } - - return deepSaveTask.OnSuccess(_ => - { - return toAwait; - }).Unwrap().OnSuccess(_ => - { - return ObjectController.SaveAsync(state, - currentOperations, - sessionToken, - cancellationToken); - }).Unwrap().ContinueWith(t => - { - if (t.IsFaulted || t.IsCanceled) - { - HandleFailedSave(currentOperations); - } - else - { - var serverState = t.Result; - HandleSave(serverState); - } - return t; - }).Unwrap(); - } - - /// - /// Saves this object to the server. - /// - public virtual Task SaveAsync() - { - return SaveAsync(CancellationToken.None); - } - - /// - /// Saves this object to the server. - /// - /// The cancellation token. - public virtual Task SaveAsync(CancellationToken cancellationToken) - { - return taskQueue.Enqueue(toAwait => SaveAsync(toAwait, cancellationToken), - cancellationToken); - } - - internal virtual Task FetchAsyncInternal( - Task toAwait, - IDictionary queryString, - CancellationToken cancellationToken) - { - return toAwait.OnSuccess(_ => - { - if (ObjectId == null) - { - throw new InvalidOperationException("Cannot refresh an object that hasn't been saved to the server."); - } - if (queryString == null) - { - queryString = new Dictionary(); - } - - return ObjectController.FetchAsync(state, queryString, AVUser.CurrentSessionToken, cancellationToken); - }).Unwrap().OnSuccess(t => - { - HandleFetchResult(t.Result); - return this; - }); - } - - private static Task DeepSaveAsync(object obj, string sessionToken, CancellationToken cancellationToken) - { - var objects = new List(); - CollectDirtyChildren(obj, objects); - - var uniqueObjects = new HashSet(objects, - new IdentityEqualityComparer()); - - var saveDirtyFileTasks = DeepTraversal(obj, true) - .OfType() - .Where(f => f.IsDirty) - .Select(f => f.SaveAsync(cancellationToken)).ToList(); - - return Task.WhenAll(saveDirtyFileTasks).OnSuccess(_ => - { - IEnumerable remaining = new List(uniqueObjects); - return InternalExtensions.WhileAsync(() => Task.FromResult(remaining.Any()), () => - { - // Partition the objects into two sets: those that can be saved immediately, - // and those that rely on other objects to be created first. - var current = (from item in remaining - where item.CanBeSerialized - select item).ToList(); - var nextBatch = (from item in remaining - where !item.CanBeSerialized - select item).ToList(); - remaining = nextBatch; - - if (current.Count == 0) - { - // We do cycle-detection when building the list of objects passed to this - // function, so this should never get called. But we should check for it - // anyway, so that we get an exception instead of an infinite loop. - throw new InvalidOperationException( - "Unable to save a AVObject with a relation to a cycle."); - } - - // Save all of the objects in current. - return AVObject.EnqueueForAll(current, toAwait => - { - return toAwait.OnSuccess(__ => - { - var states = (from item in current - select item.state).ToList(); - var operationsList = (from item in current - select item.StartSave()).ToList(); - - var saveTasks = ObjectController.SaveAllAsync(states, - operationsList, - sessionToken, - cancellationToken); - - return Task.WhenAll(saveTasks).ContinueWith(t => - { - if (t.IsFaulted || t.IsCanceled) - { - foreach (var pair in current.Zip(operationsList, (item, ops) => new { item, ops })) - { - pair.item.HandleFailedSave(pair.ops); - } - } - else - { - var serverStates = t.Result; - foreach (var pair in current.Zip(serverStates, (item, state) => new { item, state })) - { - pair.item.HandleSave(pair.state); - } - } - cancellationToken.ThrowIfCancellationRequested(); - return t; - }).Unwrap(); - }).Unwrap().OnSuccess(t => (object)null); - }, cancellationToken); - }); - }).Unwrap(); - } - - /// - /// Saves each object in the provided list. - /// - /// The objects to save. - public static Task SaveAllAsync(IEnumerable objects) where T : AVObject - { - return SaveAllAsync(objects, CancellationToken.None); - } - - /// - /// Saves each object in the provided list. - /// - /// The objects to save. - /// The cancellation token. - public static Task SaveAllAsync( - IEnumerable objects, CancellationToken cancellationToken) where T : AVObject - { - return DeepSaveAsync(objects.ToList(), AVUser.CurrentSessionToken, cancellationToken); - } - - #endregion - - #region Fetch Object(s) - - /// - /// Fetches this object with the data from the server. - /// - /// The cancellation token. - internal Task FetchAsyncInternal(CancellationToken cancellationToken) - { - return FetchAsyncInternal(null, cancellationToken); - } - - internal Task FetchAsyncInternal(IDictionary queryString, CancellationToken cancellationToken) - { - return taskQueue.Enqueue(toAwait => FetchAsyncInternal(toAwait, queryString, cancellationToken), - cancellationToken); - } - - internal Task FetchIfNeededAsyncInternal( - Task toAwait, CancellationToken cancellationToken) - { - if (!IsDataAvailable) - { - return FetchAsyncInternal(toAwait, null, cancellationToken); - } - return Task.FromResult(this); - } - - /// - /// If this AVObject has not been fetched (i.e. returns - /// false), fetches this object with the data from the server. - /// - /// The cancellation token. - internal Task FetchIfNeededAsyncInternal(CancellationToken cancellationToken) - { - return taskQueue.Enqueue(toAwait => FetchIfNeededAsyncInternal(toAwait, cancellationToken), - cancellationToken); - } - - /// - /// Fetches all of the objects that don't have data in the provided list. - /// - /// The list passed in for convenience. - public static Task> FetchAllIfNeededAsync( - IEnumerable objects) where T : AVObject - { - return FetchAllIfNeededAsync(objects, CancellationToken.None); - } - - /// - /// Fetches all of the objects that don't have data in the provided list. - /// - /// The objects to fetch. - /// The cancellation token. - /// The list passed in for convenience. - public static Task> FetchAllIfNeededAsync( - IEnumerable objects, CancellationToken cancellationToken) where T : AVObject - { - return AVObject.EnqueueForAll(objects.Cast(), (Task toAwait) => - { - return FetchAllInternalAsync(objects, false, toAwait, cancellationToken); - }, cancellationToken); - } - - /// - /// Fetches all of the objects in the provided list. - /// - /// The objects to fetch. - /// The list passed in for convenience. - public static Task> FetchAllAsync( - IEnumerable objects) where T : AVObject - { - return FetchAllAsync(objects, CancellationToken.None); - } - - /// - /// Fetches all of the objects in the provided list. - /// - /// The objects to fetch. - /// The cancellation token. - /// The list passed in for convenience. - public static Task> FetchAllAsync( - IEnumerable objects, CancellationToken cancellationToken) where T : AVObject - { - return AVObject.EnqueueForAll(objects.Cast(), (Task toAwait) => - { - return FetchAllInternalAsync(objects, true, toAwait, cancellationToken); - }, cancellationToken); - } - - /// - /// Fetches all of the objects in the list. - /// - /// The objects to fetch. - /// If false, only objects without data will be fetched. - /// A task to await before starting. - /// The cancellation token. - /// The list passed in for convenience. - private static Task> FetchAllInternalAsync( - IEnumerable objects, bool force, Task toAwait, CancellationToken cancellationToken) where T : AVObject - { - return toAwait.OnSuccess(_ => - { - if (objects.Any(obj => { return obj.state.ObjectId == null; })) - { - throw new InvalidOperationException("You cannot fetch objects that haven't already been saved."); - } - - var objectsToFetch = (from obj in objects - where force || !obj.IsDataAvailable - select obj).ToList(); - - if (objectsToFetch.Count == 0) - { - return Task.FromResult(objects); - } - - // Do one Find for each class. - var findsByClass = - (from obj in objectsToFetch - group obj.ObjectId by obj.ClassName into classGroup - where classGroup.Count() > 0 - select new - { - ClassName = classGroup.Key, - FindTask = new AVQuery(classGroup.Key) - .WhereContainedIn("objectId", classGroup) - .FindAsync(cancellationToken) - }).ToDictionary(pair => pair.ClassName, pair => pair.FindTask); - - // Wait for all the Finds to complete. - return Task.WhenAll(findsByClass.Values.ToList()).OnSuccess(__ => - { - if (cancellationToken.IsCancellationRequested) - { - return objects; - } - - // Merge the data from the Finds into the input objects. - var pairs = from obj in objectsToFetch - from result in findsByClass[obj.ClassName].Result - where result.ObjectId == obj.ObjectId - select new { obj, result }; - foreach (var pair in pairs) - { - pair.obj.MergeFromObject(pair.result); - pair.obj.hasBeenFetched = true; - } - - return objects; - }); - }).Unwrap(); - } - - #endregion - - #region Delete Object - - internal Task DeleteAsync(Task toAwait, CancellationToken cancellationToken) - { - if (ObjectId == null) - { - return Task.FromResult(0); - } - - string sessionToken = AVUser.CurrentSessionToken; - - return toAwait.OnSuccess(_ => - { - return ObjectController.DeleteAsync(State, sessionToken, cancellationToken); - }).Unwrap().OnSuccess(_ => IsDirty = true); - } - - /// - /// Deletes this object on the server. - /// - public Task DeleteAsync() - { - return DeleteAsync(CancellationToken.None); - } - - /// - /// Deletes this object on the server. - /// - /// The cancellation token. - public Task DeleteAsync(CancellationToken cancellationToken) - { - return taskQueue.Enqueue(toAwait => DeleteAsync(toAwait, cancellationToken), - cancellationToken); - } - - /// - /// Deletes each object in the provided list. - /// - /// The objects to delete. - public static Task DeleteAllAsync(IEnumerable objects) where T : AVObject - { - return DeleteAllAsync(objects, CancellationToken.None); - } - - /// - /// Deletes each object in the provided list. - /// - /// The objects to delete. - /// The cancellation token. - public static Task DeleteAllAsync( - IEnumerable objects, CancellationToken cancellationToken) where T : AVObject - { - var uniqueObjects = new HashSet(objects.OfType().ToList(), - new IdentityEqualityComparer()); - - return AVObject.EnqueueForAll(uniqueObjects, toAwait => - { - var states = uniqueObjects.Select(t => t.state).ToList(); - return toAwait.OnSuccess(_ => - { - var deleteTasks = ObjectController.DeleteAllAsync(states, - AVUser.CurrentSessionToken, - cancellationToken); - - return Task.WhenAll(deleteTasks); - }).Unwrap().OnSuccess(t => - { - // Dirty all objects in memory. - foreach (var obj in uniqueObjects) - { - obj.IsDirty = true; - } - - return (object)null; - }); - }, cancellationToken); - } - - #endregion - - private static void CollectDirtyChildren(object node, - IList dirtyChildren, - ICollection seen, - ICollection seenNew) - { - foreach (var obj in DeepTraversal(node).OfType()) - { - ICollection scopedSeenNew; - // Check for cycles of new objects. Any such cycle means it will be impossible to save - // this collection of objects, so throw an exception. - if (obj.ObjectId != null) - { - scopedSeenNew = new HashSet(new IdentityEqualityComparer()); - } - else - { - if (seenNew.Contains(obj)) - { - throw new InvalidOperationException("Found a circular dependency while saving"); - } - scopedSeenNew = new HashSet(seenNew, new IdentityEqualityComparer()); - scopedSeenNew.Add(obj); - } - - // Check for cycles of any object. If this occurs, then there's no problem, but - // we shouldn't recurse any deeper, because it would be an infinite recursion. - if (seen.Contains(obj)) - { - return; - } - seen.Add(obj); - - // Recurse into this object's children looking for dirty children. - // We only need to look at the child object's current estimated data, - // because that's the only data that might need to be saved now. - CollectDirtyChildren(obj.estimatedData, dirtyChildren, seen, scopedSeenNew); - - if (obj.CheckIsDirty(false)) - { - dirtyChildren.Add(obj); - } - } - } - - /// - /// Helper version of CollectDirtyChildren so that callers don't have to add the internally - /// used parameters. - /// - private static void CollectDirtyChildren(object node, IList dirtyChildren) - { - CollectDirtyChildren(node, - dirtyChildren, - new HashSet(new IdentityEqualityComparer()), - new HashSet(new IdentityEqualityComparer())); - } - - /// - /// Returns true if the given object can be serialized for saving as a value - /// that is pointed to by a AVObject. - /// - private static bool CanBeSerializedAsValue(object value) - { - return DeepTraversal(value, yieldRoot: true) - .OfType() - .All(o => o.ObjectId != null); - } - - private bool CanBeSerialized - { - get - { - // This method is only used for batching sets of objects for saveAll - // and when saving children automatically. Since it's only used to - // determine whether or not save should be called on them, it only - // needs to examine their current values, so we use estimatedData. - lock (mutex) - { - return CanBeSerializedAsValue(estimatedData); - } - } - } - - /// - /// Adds a task to the queue for all of the given objects. - /// - private static Task EnqueueForAll(IEnumerable objects, - Func> taskStart, CancellationToken cancellationToken) - { - // The task that will be complete when all of the child queues indicate they're ready to start. - var readyToStart = new TaskCompletionSource(); - - // First, we need to lock the mutex for the queue for every object. We have to hold this - // from at least when taskStart() is called to when obj.taskQueue enqueue is called, so - // that saves actually get executed in the order they were setup by taskStart(). - // The locks have to be sorted so that we always acquire them in the same order. - // Otherwise, there's some risk of deadlock. - var lockSet = new LockSet(objects.Select(o => o.taskQueue.Mutex)); - - lockSet.Enter(); - try - { - - // The task produced by taskStart. By running this immediately, we allow everything prior - // to toAwait to run before waiting for all of the queues on all of the objects. - Task fullTask = taskStart(readyToStart.Task); - - // Add fullTask to each of the objects' queues. - var childTasks = new List(); - foreach (AVObject obj in objects) - { - obj.taskQueue.Enqueue((Task task) => - { - childTasks.Add(task); - return fullTask; - }, cancellationToken); - } - - // When all of the objects' queues are ready, signal fullTask that it's ready to go on. - Task.WhenAll(childTasks.ToArray()).ContinueWith((Task task) => - { - readyToStart.SetResult(null); - }); - - return fullTask; - } - finally - { - lockSet.Exit(); - } - } - - /// - /// Removes a key from the object's data if it exists. - /// - /// The key to remove. - public virtual void Remove(string key) - { - lock (mutex) - { - CheckKeyIsMutable(key); - - PerformOperation(key, AVDeleteOperation.Instance); - } - } - - - private IEnumerable ApplyOperations(IDictionary operations, - IDictionary map) - { - List appliedKeys = new List(); - lock (mutex) - { - foreach (var pair in operations) - { - object oldValue; - map.TryGetValue(pair.Key, out oldValue); - var newValue = pair.Value.Apply(oldValue, pair.Key); - if (newValue != AVDeleteOperation.DeleteToken) - { - map[pair.Key] = newValue; - } - else - { - map.Remove(pair.Key); - } - appliedKeys.Add(pair.Key); - } - } - return appliedKeys; - } - - /// - /// Regenerates the estimatedData map from the serverData and operations. - /// - internal void RebuildEstimatedData() - { - IEnumerable changedKeys = null; - - lock (mutex) - { - //estimatedData.Clear(); - List converdKeys = new List(); - foreach (var item in state) - { - var key = item.Key; - var value = item.Value; - if (!estimatedData.ContainsKey(key)) - { - converdKeys.Add(key); - } - else - { - var oldValue = estimatedData[key]; - if (oldValue != value) - { - converdKeys.Add(key); - } - estimatedData.Remove(key); - } - estimatedData.Add(item); - } - changedKeys = converdKeys; - foreach (var operations in operationSetQueue) - { - var appliedKeys = ApplyOperations(operations, estimatedData); - changedKeys = converdKeys.Concat(appliedKeys); - } - // We've just applied a bunch of operations to estimatedData which - // may have changed all of its keys. Notify of all keys and properties - // mapped to keys being changed. - OnFieldsChanged(changedKeys); - } - } - - /// - /// PerformOperation is like setting a value at an index, but instead of - /// just taking a new value, it takes a AVFieldOperation that modifies the value. - /// - internal void PerformOperation(string key, IAVFieldOperation operation) - { - lock (mutex) - { - var ifDirtyBeforePerform = this.IsDirty; - object oldValue; - estimatedData.TryGetValue(key, out oldValue); - object newValue = operation.Apply(oldValue, key); - if (newValue != AVDeleteOperation.DeleteToken) - { - estimatedData[key] = newValue; - } - else - { - estimatedData.Remove(key); - } - - IAVFieldOperation oldOperation; - bool wasDirty = CurrentOperations.Count > 0; - CurrentOperations.TryGetValue(key, out oldOperation); - var newOperation = operation.MergeWithPrevious(oldOperation); - CurrentOperations[key] = newOperation; - if (!wasDirty) - { - OnPropertyChanged("IsDirty"); - if (ifDirtyBeforePerform != wasDirty) - { - OnPropertyUpdated("IsDirty", ifDirtyBeforePerform, wasDirty); - } - } - - OnFieldsChanged(new[] { key }); - OnPropertyUpdated(key, oldValue, newValue); - } - } - - /// - /// Override to run validations on key/value pairs. Make sure to still - /// call the base version. - /// - internal virtual void OnSettingValue(ref string key, ref object value) - { - if (key == null) - { - throw new ArgumentNullException("key"); - } - } - - /// - /// Gets or sets a value on the object. It is recommended to name - /// keys in partialCamelCaseLikeThis. - /// - /// The key for the object. Keys must be alphanumeric plus underscore - /// and start with a letter. - /// The property is - /// retrieved and is not found. - /// The value for the key. - virtual public object this[string key] - { - get - { - lock (mutex) - { - CheckGetAccess(key); - - var value = estimatedData[key]; - - // A relation may be deserialized without a parent or key. Either way, - // make sure it's consistent. - var relation = value as AVRelationBase; - if (relation != null) - { - relation.EnsureParentAndKey(this, key); - } - - return value; - } - } - set - { - lock (mutex) - { - CheckKeyIsMutable(key); - - Set(key, value); - } - } - } - - /// - /// Perform Set internally which is not gated by mutability check. - /// - /// key for the object. - /// the value for the key. - internal void Set(string key, object value) - { - lock (mutex) - { - OnSettingValue(ref key, ref value); - - if (!AVEncoder.IsValidType(value)) - { - throw new ArgumentException("Invalid type for value: " + value.GetType().ToString()); - } - - PerformOperation(key, new AVSetOperation(value)); - } - } - - internal void SetIfDifferent(string key, T value) - { - T current; - bool hasCurrent = TryGetValue(key, out current); - if (value == null) - { - if (hasCurrent) - { - PerformOperation(key, AVDeleteOperation.Instance); - } - return; - } - if (!hasCurrent || !value.Equals(current)) - { - Set(key, value); - } - } - - #region Atomic Increment - - /// - /// Atomically increments the given key by 1. - /// - /// The key to increment. - public void Increment(string key) - { - Increment(key, 1); - } - - /// - /// Atomically increments the given key by the given number. - /// - /// The key to increment. - /// The amount to increment by. - public void Increment(string key, long amount) - { - lock (mutex) - { - CheckKeyIsMutable(key); - - PerformOperation(key, new AVIncrementOperation(amount)); - } - } - - /// - /// Atomically increments the given key by the given number. - /// - /// The key to increment. - /// The amount to increment by. - public void Increment(string key, double amount) - { - lock (mutex) - { - CheckKeyIsMutable(key); - - PerformOperation(key, new AVIncrementOperation(amount)); - } - } - - #endregion - - /// - /// Atomically adds an object to the end of the list associated with the given key. - /// - /// The key. - /// The object to add. - public void AddToList(string key, object value) - { - AddRangeToList(key, new[] { value }); - } - - /// - /// Atomically adds objects to the end of the list associated with the given key. - /// - /// The key. - /// The objects to add. - public void AddRangeToList(string key, IEnumerable values) - { - lock (mutex) - { - CheckKeyIsMutable(key); - - PerformOperation(key, new AVAddOperation(values.Cast())); - - OnCollectionPropertyUpdated(key, NotifyCollectionUpdatedAction.Add, null, values); - } - } - - /// - /// Atomically adds an object to the end of the list associated with the given key, - /// only if it is not already present in the list. The position of the insert is not - /// guaranteed. - /// - /// The key. - /// The object to add. - public void AddUniqueToList(string key, object value) - { - AddRangeUniqueToList(key, new object[] { value }); - } - - /// - /// Atomically adds objects to the end of the list associated with the given key, - /// only if they are not already present in the list. The position of the inserts are not - /// guaranteed. - /// - /// The key. - /// The objects to add. - public void AddRangeUniqueToList(string key, IEnumerable values) - { - lock (mutex) - { - CheckKeyIsMutable(key); - - PerformOperation(key, new AVAddUniqueOperation(values.Cast())); - } - } - - /// - /// Atomically removes all instances of the objects in - /// from the list associated with the given key. - /// - /// The key. - /// The objects to remove. - public void RemoveAllFromList(string key, IEnumerable values) - { - lock (mutex) - { - CheckKeyIsMutable(key); - - PerformOperation(key, new AVRemoveOperation(values.Cast())); - - OnCollectionPropertyUpdated(key, NotifyCollectionUpdatedAction.Remove, values, null); - } - } - - /// - /// Returns whether this object has a particular key. - /// - /// The key to check for - public bool ContainsKey(string key) - { - lock (mutex) - { - return estimatedData.ContainsKey(key); - } - } - - /// - /// Gets a value for the key of a particular type. - /// The type to convert the value to. Supported types are - /// AVObject and its descendents, LeanCloud types such as AVRelation and AVGeopoint, - /// primitive types,IList<T>, IDictionary<string, T>, and strings. - /// The key of the element to get. - /// The property is - /// retrieved and is not found. - /// - public T Get(string key) - { - return Conversion.To(this[key]); - } - - /// - /// Access or create a Relation value for a key. - /// - /// The type of object to create a relation for. - /// The key for the relation field. - /// A AVRelation for the key. - public AVRelation GetRelation(string key) where T : AVObject - { - // All the sanity checking is done when add or remove is called. - AVRelation relation = null; - TryGetValue(key, out relation); - return relation ?? new AVRelation(this, key); - } - - /// - /// Get relation revserse query. - /// - /// AVObject - /// parent className - /// key - /// - public AVQuery GetRelationRevserseQuery(string parentClassName, string key) where T : AVObject - { - if (string.IsNullOrEmpty(parentClassName)) - { - throw new ArgumentNullException("parentClassName", "can not query a relation without parentClassName."); - } - if (string.IsNullOrEmpty(key)) - { - throw new ArgumentNullException("key", "can not query a relation without key."); - } - return new AVQuery(parentClassName).WhereEqualTo(key, this); - } - - /// - /// Populates result with the value for the key, if possible. - /// - /// The desired type for the value. - /// The key to retrieve a value for. - /// The value for the given key, converted to the - /// requested type, or null if unsuccessful. - /// true if the lookup and conversion succeeded, otherwise - /// false. - public virtual bool TryGetValue(string key, out T result) - { - lock (mutex) - { - if (ContainsKey(key)) - { - try - { - var temp = Conversion.To(this[key]); - result = temp; - return true; - } - catch (InvalidCastException) - { - result = default(T); - return false; - } - } - result = default(T); - return false; - } - } - - /// - /// Gets whether the AVObject has been fetched. - /// - public bool IsDataAvailable - { - get - { - lock (mutex) - { - return hasBeenFetched; - } - } - } - - private bool CheckIsDataAvailable(string key) - { - lock (mutex) - { - return IsDataAvailable || estimatedData.ContainsKey(key); - } - } - - private void CheckGetAccess(string key) - { - lock (mutex) - { - if (!CheckIsDataAvailable(key)) - { - throw new InvalidOperationException( - "AVObject has no data for this key. Call FetchIfNeededAsync() to get the data."); - } - } - } - - private void CheckKeyIsMutable(string key) - { - if (!IsKeyMutable(key)) - { - throw new InvalidOperationException( - "Cannot change the `" + key + "` property of a `" + ClassName + "` object."); - } - } - - protected virtual bool IsKeyMutable(string key) - { - return true; - } - - /// - /// A helper function for checking whether two AVObjects point to - /// the same object in the cloud. - /// - public bool HasSameId(AVObject other) - { - lock (mutex) - { - return other != null && - object.Equals(ClassName, other.ClassName) && - object.Equals(ObjectId, other.ObjectId); - } - } - - internal IDictionary CurrentOperations - { - get - { - lock (mutex) - { - return operationSetQueue.Last.Value; - } - } - } - - /// - /// Gets a set view of the keys contained in this object. This does not include createdAt, - /// updatedAt, or objectId. It does include things like username and ACL. - /// - public ICollection Keys - { - get - { - lock (mutex) - { - return estimatedData.Keys; - } - } - } - - /// - /// Gets or sets the AVACL governing this object. - /// - [AVFieldName("ACL")] - public AVACL ACL - { - get { return GetProperty(null, "ACL"); } - set { SetProperty(value, "ACL"); } - } - - /// - /// Returns true if this object was created by the LeanCloud server when the - /// object might have already been there (e.g. in the case of a Facebook - /// login) - /// -#if !UNITY - public -#else - internal -#endif - bool IsNew - { - get - { - return state.IsNew; - } -#if !UNITY - internal -#endif - set - { - MutateState(mutableClone => - { - mutableClone.IsNew = value; - }); - OnPropertyChanged("IsNew"); - } - } - - /// - /// Gets the last time this object was updated as the server sees it, so that if you make changes - /// to a AVObject, then wait a while, and then call , the updated time - /// will be the time of the call rather than the time the object was - /// changed locally. - /// - [AVFieldName("updatedAt")] - public DateTime? UpdatedAt - { - get - { - return state.UpdatedAt; - } - } - - /// - /// Gets the first time this object was saved as the server sees it, so that if you create a - /// AVObject, then wait a while, and then call , the - /// creation time will be the time of the first call rather than - /// the time the object was created locally. - /// - [AVFieldName("createdAt")] - public DateTime? CreatedAt - { - get - { - return state.CreatedAt; - } - } - - /// - /// Indicates whether this AVObject has unsaved changes. - /// - public bool IsDirty - { - get - { - lock (mutex) { return CheckIsDirty(true); } - } - internal set - { - lock (mutex) - { - dirty = value; - OnPropertyChanged("IsDirty"); - } - } - } - - /// - /// Indicates whether key is unsaved for this AVObject. - /// - /// The key to check for. - /// true if the key has been altered and not saved yet, otherwise - /// false. - public bool IsKeyDirty(string key) - { - lock (mutex) - { - return CurrentOperations.ContainsKey(key); - } - } - - private bool CheckIsDirty(bool considerChildren) - { - lock (mutex) - { - return (dirty || CurrentOperations.Count > 0 || (considerChildren && HasDirtyChildren)); - } - } - - /// - /// Gets or sets the object id. An object id is assigned as soon as an object is - /// saved to the server. The combination of a and an - /// uniquely identifies an object in your application. - /// - [AVFieldName("objectId")] - public string ObjectId - { - get - { - return state.ObjectId; - } - set - { - IsDirty = true; - SetObjectIdInternal(value); - } - } - /// - /// Sets the objectId without marking dirty. - /// - /// The new objectId - private void SetObjectIdInternal(string objectId) - { - lock (mutex) - { - MutateState(mutableClone => - { - mutableClone.ObjectId = objectId; - }); - OnPropertyChanged("ObjectId"); - } - } - - /// - /// Gets the class name for the AVObject. - /// - public string ClassName - { - get - { - return state.ClassName; - } - } - - /// - /// Adds a value for the given key, throwing an Exception if the key - /// already has a value. - /// - /// - /// This allows you to use collection initialization syntax when creating AVObjects, - /// such as: - /// - /// var obj = new AVObject("MyType") - /// { - /// {"name", "foo"}, - /// {"count", 10}, - /// {"found", false} - /// }; - /// - /// - /// The key for which a value should be set. - /// The value for the key. - public void Add(string key, object value) - { - lock (mutex) - { - if (this.ContainsKey(key)) - { - throw new ArgumentException("Key already exists", key); - } - this[key] = value; - } - } - - IEnumerator> IEnumerable> - .GetEnumerator() - { - lock (mutex) - { - return estimatedData.GetEnumerator(); - } - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - lock (mutex) - { - return ((IEnumerable>)this).GetEnumerator(); - } - } - - /// - /// Gets a for the type of object specified by - /// - /// - /// The class name of the object. - /// A new . - public static AVQuery GetQuery(string className) - { - // Since we can't return a AVQuery (due to strong-typing with - // generics), we'll require you to go through subclasses. This is a better - // experience anyway, especially with LINQ integration, since you'll get - // strongly-typed queries and compile-time checking of property names and - // types. - if (SubclassingController.GetType(className) != null) - { - throw new ArgumentException( - "Use the class-specific query properties for class " + className, "className"); - } - return new AVQuery(className); - } - - /// - /// Raises change notifications for all properties associated with the given - /// field names. If fieldNames is null, this will notify for all known field-linked - /// properties (e.g. this happens when we recalculate all estimated data from scratch) - /// - protected virtual void OnFieldsChanged(IEnumerable fieldNames) - { - var mappings = SubclassingController.GetPropertyMappings(ClassName); - IEnumerable properties; - - if (fieldNames != null && mappings != null) - { - properties = from m in mappings - join f in fieldNames on m.Value equals f - select m.Key; - } - else if (mappings != null) - { - properties = mappings.Keys; - } - else - { - properties = Enumerable.Empty(); - } - - foreach (var property in properties) - { - OnPropertyChanged(property); - } - OnPropertyChanged("Item[]"); - } - - /// - /// Raises change notifications for a property. Passing null or the empty string - /// notifies the binding framework that all properties/indexes have changed. - /// Passing "Item[]" tells the binding framework that all indexed values - /// have changed (but not all properties) - /// - protected virtual void OnPropertyChanged( -#if !UNITY -[CallerMemberName] string propertyName = null -#else -string propertyName -#endif -) - { - propertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - - private SynchronizedEventHandler propertyChanged = - new SynchronizedEventHandler(); - /// - /// Occurs when a property value changes. - /// - public event PropertyChangedEventHandler PropertyChanged - { - add - { - propertyChanged.Add(value); - } - remove - { - propertyChanged.Remove(value); - } - } - - private SynchronizedEventHandler propertyUpdated = - new SynchronizedEventHandler(); - - public event PropertyUpdatedEventHandler PropertyUpdated - { - add - { - propertyUpdated.Add(value); - } - remove - { - propertyUpdated.Remove(value); - } - } - - protected virtual void OnPropertyUpdated(string propertyName, object newValue, object oldValue) - { - propertyUpdated.Invoke(this, new PropertyUpdatedEventArgs(propertyName, oldValue, newValue)); - } - - private SynchronizedEventHandler collectionUpdated = -new SynchronizedEventHandler(); - - public event CollectionPropertyUpdatedEventHandler CollectionPropertyUpdated - { - add - { - collectionUpdated.Add(value); - } - remove - { - collectionUpdated.Remove(value); - } - } - - protected virtual void OnCollectionPropertyUpdated(string propertyName, NotifyCollectionUpdatedAction action, IEnumerable oldValues, IEnumerable newValues) - { - collectionUpdated?.Invoke(this, new CollectionPropertyUpdatedEventArgs(propertyName, action, oldValues, newValues)); - } - } - - public interface INotifyPropertyUpdated - { - event PropertyUpdatedEventHandler PropertyUpdated; - } - - public interface INotifyCollectionPropertyUpdated - { - event CollectionPropertyUpdatedEventHandler CollectionPropertyUpdated; - } - - public enum NotifyCollectionUpdatedAction - { - Add, - Remove - } - - public class CollectionPropertyUpdatedEventArgs : PropertyChangedEventArgs - { - public CollectionPropertyUpdatedEventArgs(string propertyName, NotifyCollectionUpdatedAction collectionAction, IEnumerable oldValues, IEnumerable newValues) : base(propertyName) - { - CollectionAction = collectionAction; - OldValues = oldValues; - NewValues = newValues; - } - - public IEnumerable OldValues { get; set; } - - public IEnumerable NewValues { get; set; } - - public NotifyCollectionUpdatedAction CollectionAction { get; set; } - } - - public class PropertyUpdatedEventArgs : PropertyChangedEventArgs - { - public PropertyUpdatedEventArgs(string propertyName, object oldValue, object newValue) : base(propertyName) - { - OldValue = oldValue; - NewValue = newValue; - } - - public object OldValue { get; private set; } - public object NewValue { get; private set; } - } - - public delegate void PropertyUpdatedEventHandler(object sender, PropertyUpdatedEventArgs args); - - public delegate void CollectionPropertyUpdatedEventHandler(object sender, CollectionPropertyUpdatedEventArgs args); -} diff --git a/Storage/Source/Public/AVQuery.cs b/Storage/Source/Public/AVQuery.cs deleted file mode 100644 index 9c85f8a..0000000 --- a/Storage/Source/Public/AVQuery.cs +++ /dev/null @@ -1,360 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Text.RegularExpressions; -using System.Threading; -using System.Threading.Tasks; -using LeanCloud.Storage.Internal; - -namespace LeanCloud -{ - - /// - /// The AVQuery class defines a query that is used to fetch AVObjects. The - /// most common use case is finding all objects that match a query through the - /// method. - /// - /// - /// This sample code fetches all objects of - /// class "MyClass": - /// - /// - /// AVQuery query = new AVQuery("MyClass"); - /// IEnumerable<AVObject> result = await query.FindAsync(); - /// - /// - /// A AVQuery can also be used to retrieve a single object whose id is known, - /// through the method. For example, this sample code - /// fetches an object of class "MyClass" and id myId. - /// - /// - /// AVQuery query = new AVQuery("MyClass"); - /// AVObject result = await query.GetAsync(myId); - /// - /// - /// A AVQuery can also be used to count the number of objects that match the - /// query without retrieving all of those objects. For example, this sample code - /// counts the number of objects of the class "MyClass". - /// - /// - /// AVQuery query = new AVQuery("MyClass"); - /// int count = await query.CountAsync(); - /// - /// - public class AVQuery : AVQueryPair, T>, IAVQuery - where T : AVObject - { - internal static IAVQueryController QueryController - { - get - { - return AVPlugins.Instance.QueryController; - } - } - - internal static IObjectSubclassingController SubclassingController - { - get - { - return AVPlugins.Instance.SubclassingController; - } - } - - /// - /// 调试时可以用来查看最终的发送的查询语句 - /// - private string JsonString - { - get - { - return AVClient.SerializeJsonString(this.BuildParameters(true)); - } - } - - /// - /// Private constructor for composition of queries. A Source query is required, - /// but the remaining values can be null if they won't be changed in this - /// composition. - /// - private AVQuery(AVQuery source, - IDictionary where = null, - IEnumerable replacementOrderBy = null, - IEnumerable thenBy = null, - int? skip = null, - int? limit = null, - IEnumerable includes = null, - IEnumerable selectedKeys = null, - string redirectClassNameForKey = null) - : base(source, where, replacementOrderBy, thenBy, skip, limit, includes, selectedKeys, redirectClassNameForKey) - { - - } - - //internal override AVQuery CreateInstance( - // AVQuery source, - // IDictionary where = null, - // IEnumerable replacementOrderBy = null, - // IEnumerable thenBy = null, - // int? skip = null, - // int? limit = null, - // IEnumerable includes = null, - // IEnumerable selectedKeys = null, - // String redirectClassNameForKey = null) - //{ - // return new AVQuery(this, where, replacementOrderBy, thenBy, skip, limit, includes, selectedKeys, redirectClassNameForKey); - //} - - public override AVQuery CreateInstance( - IDictionary where = null, - IEnumerable replacementOrderBy = null, - IEnumerable thenBy = null, - int? skip = null, - int? limit = null, - IEnumerable includes = null, - IEnumerable selectedKeys = null, - string redirectClassNameForKey = null) - { - return new AVQuery(this, where, replacementOrderBy, thenBy, skip, limit, includes, selectedKeys, redirectClassNameForKey); - } - - - /// - /// Constructs a query based upon the AVObject subclass used as the generic parameter for the AVQuery. - /// - public AVQuery() - : this(SubclassingController.GetClassName(typeof(T))) - { - } - - /// - /// Constructs a query. A default query with no further parameters will retrieve - /// all s of the provided class. - /// - /// The name of the class to retrieve AVObjects for. - public AVQuery(string className) - : base(className) - { - - } - - /// - /// Constructs a query that is the or of the given queries. - /// - /// The list of AVQueries to 'or' together. - /// A AVQquery that is the 'or' of the passed in queries. - public static AVQuery Or(IEnumerable> queries) - { - string className = null; - var orValue = new List>(); - // We need to cast it to non-generic IEnumerable because of AOT-limitation - var nonGenericQueries = (IEnumerable)queries; - foreach (var obj in nonGenericQueries) - { - var q = (AVQuery)obj; - if (className != null && q.className != className) - { - throw new ArgumentException( - "All of the queries in an or query must be on the same class."); - } - className = q.className; - var parameters = q.BuildParameters(); - if (parameters.Count == 0) - { - continue; - } - object where; - if (!parameters.TryGetValue("where", out where) || parameters.Count > 1) - { - throw new ArgumentException( - "None of the queries in an or query can have non-filtering clauses"); - } - orValue.Add(where as IDictionary); - } - return new AVQuery(new AVQuery(className), - where: new Dictionary { - { "$or", orValue} - }); - } - - /// - /// Retrieves a list of AVObjects that satisfy this query from LeanCloud. - /// - /// The cancellation token. - /// The list of AVObjects that match this query. - public override Task> FindAsync(CancellationToken cancellationToken) - { - return AVUser.GetCurrentUserAsync().OnSuccess(t => - { - return QueryController.FindAsync(this, t.Result, cancellationToken); - }).Unwrap().OnSuccess(t => - { - IEnumerable states = t.Result; - return (from state in states - select AVObject.FromState(state, ClassName)); - }); - } - - /// - /// Retrieves at most one AVObject that satisfies this query. - /// - /// The cancellation token. - /// A single AVObject that satisfies this query, or else null. - public override Task FirstOrDefaultAsync(CancellationToken cancellationToken) - { - return AVUser.GetCurrentUserAsync().OnSuccess(t => - { - return QueryController.FirstAsync(this, t.Result, cancellationToken); - }).Unwrap().OnSuccess(t => - { - IObjectState state = t.Result; - return state == null ? default(T) : AVObject.FromState(state, ClassName); - }); - } - - /// - /// Retrieves at most one AVObject that satisfies this query. - /// - /// The cancellation token. - /// A single AVObject that satisfies this query. - /// If no results match the query. - public override Task FirstAsync(CancellationToken cancellationToken) - { - return FirstOrDefaultAsync(cancellationToken).OnSuccess(t => - { - if (t.Result == null) - { - throw new AVException(AVException.ErrorCode.ObjectNotFound, - "No results matched the query."); - } - return t.Result; - }); - } - - /// - /// Counts the number of objects that match this query. - /// - /// The cancellation token. - /// The number of objects that match this query. - public override Task CountAsync(CancellationToken cancellationToken) - { - return AVUser.GetCurrentUserAsync().OnSuccess(t => - { - return QueryController.CountAsync(this, t.Result, cancellationToken); - }).Unwrap(); - } - - /// - /// Constructs a AVObject whose id is already known by fetching data - /// from the server. - /// - /// ObjectId of the AVObject to fetch. - /// The cancellation token. - /// The AVObject for the given objectId. - public override Task GetAsync(string objectId, CancellationToken cancellationToken) - { - AVQuery singleItemQuery = new AVQuery(className) - .WhereEqualTo("objectId", objectId); - singleItemQuery = new AVQuery(singleItemQuery, includes: this.includes, selectedKeys: this.selectedKeys, limit: 1); - return singleItemQuery.FindAsync(cancellationToken).OnSuccess(t => - { - var result = t.Result.FirstOrDefault(); - if (result == null) - { - throw new AVException(AVException.ErrorCode.ObjectNotFound, - "Object with the given objectId not found."); - } - return result; - }); - } - - #region CQL - /// - /// 执行 CQL 查询 - /// - /// CQL 语句 - /// CancellationToken - /// 返回符合条件的对象集合 - public static Task> DoCloudQueryAsync(string cql, CancellationToken cancellationToken) - { - var queryString = string.Format("cloudQuery?cql={0}", Uri.EscapeDataString(cql)); - - return rebuildObjectFromCloudQueryResult(queryString); - } - - /// - /// 执行 CQL 查询 - /// - /// - /// - public static Task> DoCloudQueryAsync(string cql) - { - return DoCloudQueryAsync(cql, CancellationToken.None); - } - - /// - /// 执行 CQL 查询 - /// - /// 带有占位符的模板 cql 语句 - /// 占位符对应的参数数组 - /// - public static Task> DoCloudQueryAsync(string cqlTeamplate, params object[] pvalues) - { - string queryStringTemplate = "cloudQuery?cql={0}&pvalues={1}"; - string pSrting = Json.Encode(pvalues); - string queryString = string.Format(queryStringTemplate, Uri.EscapeDataString(cqlTeamplate), Uri.EscapeDataString(pSrting)); - - return rebuildObjectFromCloudQueryResult(queryString); - } - - internal static Task> rebuildObjectFromCloudQueryResult(string queryString) - { - var command = new AVCommand(queryString, - method: "GET", - sessionToken: AVUser.CurrentSessionToken, - data: null); - - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command, cancellationToken: CancellationToken.None).OnSuccess(t => - { - var items = t.Result.Item2["results"] as IList; - var className = t.Result.Item2["className"].ToString(); - - IEnumerable states = (from item in items - select AVObjectCoder.Instance.Decode(item as IDictionary, AVDecoder.Instance)); - - return (from state in states - select AVObject.FromState(state, className)); - }); - } - - #endregion - - /// - /// Determines whether the specified object is equal to the current object. - /// - /// The object to compare with the current object. - /// true if the specified object is equal to the current object; otherwise, false - public override bool Equals(object obj) - { - if (obj == null || !(obj is AVQuery)) - { - return false; - } - - var other = obj as AVQuery; - return Object.Equals(this.className, other.ClassName) && - this.where.CollectionsEqual(other.where) && - this.orderBy.CollectionsEqual(other.orderBy) && - this.includes.CollectionsEqual(other.includes) && - this.selectedKeys.CollectionsEqual(other.selectedKeys) && - Object.Equals(this.skip, other.skip) && - Object.Equals(this.limit, other.limit); - } - - public override int GetHashCode() - { - return base.GetHashCode(); - } - } -} diff --git a/Storage/Source/Public/AVQueryExtensions.cs b/Storage/Source/Public/AVQueryExtensions.cs deleted file mode 100644 index c726b41..0000000 --- a/Storage/Source/Public/AVQueryExtensions.cs +++ /dev/null @@ -1,818 +0,0 @@ -using LeanCloud.Storage.Internal; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; - -namespace LeanCloud -{ - /// - /// Provides extension methods for to support - /// Linq-style queries. - /// - public static class AVQueryExtensions - { - private static readonly MethodInfo getMethod; - private static readonly MethodInfo stringContains; - private static readonly MethodInfo stringStartsWith; - private static readonly MethodInfo stringEndsWith; - private static readonly MethodInfo containsMethod; - private static readonly MethodInfo notContainsMethod; - private static readonly MethodInfo containsKeyMethod; - private static readonly MethodInfo notContainsKeyMethod; - private static readonly Dictionary functionMappings; - static AVQueryExtensions() - { - getMethod = GetMethod(obj => obj.Get(null)).GetGenericMethodDefinition(); - stringContains = GetMethod(str => str.Contains(null)); - stringStartsWith = GetMethod(str => str.StartsWith(null)); - stringEndsWith = GetMethod(str => str.EndsWith(null)); - functionMappings = new Dictionary { - { - stringContains, - GetMethod>(q => q.WhereContains(null, null)) - }, - { - stringStartsWith, - GetMethod>(q => q.WhereStartsWith(null, null)) - }, - { - stringEndsWith, - GetMethod>(q => q.WhereEndsWith(null,null)) - }, - }; - containsMethod = GetMethod( - o => AVQueryExtensions.ContainsStub(null, null)).GetGenericMethodDefinition(); - notContainsMethod = GetMethod( - o => AVQueryExtensions.NotContainsStub(null, null)) - .GetGenericMethodDefinition(); - - containsKeyMethod = GetMethod(o => AVQueryExtensions.ContainsKeyStub(null, null)); - notContainsKeyMethod = GetMethod( - o => AVQueryExtensions.NotContainsKeyStub(null, null)); - } - - /// - /// Gets a MethodInfo for a top-level method call. - /// - private static MethodInfo GetMethod(Expression> expression) - { - return (expression.Body as MethodCallExpression).Method; - } - - /// - /// When a query is normalized, this is a placeholder to indicate we should - /// add a WhereContainedIn() clause. - /// - private static bool ContainsStub(object collection, T value) - { - throw new NotImplementedException( - "Exists only for expression translation as a placeholder."); - } - - /// - /// When a query is normalized, this is a placeholder to indicate we should - /// add a WhereNotContainedIn() clause. - /// - private static bool NotContainsStub(object collection, T value) - { - throw new NotImplementedException( - "Exists only for expression translation as a placeholder."); - } - - /// - /// When a query is normalized, this is a placeholder to indicate that we should - /// add a WhereExists() clause. - /// - private static bool ContainsKeyStub(AVObject obj, string key) - { - throw new NotImplementedException( - "Exists only for expression translation as a placeholder."); - } - - /// - /// When a query is normalized, this is a placeholder to indicate that we should - /// add a WhereDoesNotExist() clause. - /// - private static bool NotContainsKeyStub(AVObject obj, string key) - { - throw new NotImplementedException( - "Exists only for expression translation as a placeholder."); - } - - /// - /// Evaluates an expression and throws if the expression has components that can't be - /// evaluated (e.g. uses the parameter that's only represented by an object on the server). - /// - private static object GetValue(Expression exp) - { - try - { - return Expression.Lambda( - typeof(Func<>).MakeGenericType(exp.Type), exp).Compile().DynamicInvoke(); - } - catch (Exception e) - { - throw new InvalidOperationException("Unable to evaluate expression: " + exp, e); - } - } - - /// - /// Checks whether the MethodCallExpression is a call to AVObject.Get(), - /// which is the call we normalize all indexing into the AVObject to. - /// - private static bool IsAVObjectGet(MethodCallExpression node) - { - if (node == null || node.Object == null) - { - return false; - } - if (!typeof(AVObject).GetTypeInfo().IsAssignableFrom(node.Object.Type.GetTypeInfo())) - { - return false; - } - return node.Method.IsGenericMethod && node.Method.GetGenericMethodDefinition() == getMethod; - } - - - /// - /// Visits an Expression, converting AVObject.Get/AVObject[]/AVObject.Property, - /// and nested indices into a single call to AVObject.Get() with a "field path" like - /// "foo.bar.baz" - /// - private class ObjectNormalizer : ExpressionVisitor - { - protected override Expression VisitIndex(IndexExpression node) - { - var visitedObject = Visit(node.Object); - var indexer = visitedObject as MethodCallExpression; - if (IsAVObjectGet(indexer)) - { - var indexValue = GetValue(node.Arguments[0]) as string; - if (indexValue == null) - { - throw new InvalidOperationException("Index must be a string"); - } - var newPath = GetValue(indexer.Arguments[0]) + "." + indexValue; - return Expression.Call(indexer.Object, - getMethod.MakeGenericMethod(node.Type), - Expression.Constant(newPath, typeof(string))); - } - return base.VisitIndex(node); - } - - /// - /// Check for a AVFieldName attribute and use that as the path component, turning - /// properties like foo.ObjectId into foo.Get("objectId") - /// - protected override Expression VisitMember(MemberExpression node) - { - var fieldName = node.Member.GetCustomAttribute(); - if (fieldName != null && - typeof(AVObject).GetTypeInfo().IsAssignableFrom(node.Expression.Type.GetTypeInfo())) - { - var newPath = fieldName.FieldName; - return Expression.Call(node.Expression, - getMethod.MakeGenericMethod(node.Type), - Expression.Constant(newPath, typeof(string))); - } - return base.VisitMember(node); - } - - /// - /// If a AVObject.Get() call has been cast, just change the generic parameter. - /// - protected override Expression VisitUnary(UnaryExpression node) - { - var methodCall = Visit(node.Operand) as MethodCallExpression; - if ((node.NodeType == ExpressionType.Convert || - node.NodeType == ExpressionType.ConvertChecked) && - IsAVObjectGet(methodCall)) - { - return Expression.Call(methodCall.Object, - getMethod.MakeGenericMethod(node.Type), - methodCall.Arguments); - } - return base.VisitUnary(node); - } - - protected override Expression VisitMethodCall(MethodCallExpression node) - { - if (node.Method.Name == "get_Item" && node.Object is ParameterExpression) - { - var indexPath = GetValue(node.Arguments[0]) as string; - return Expression.Call(node.Object, - getMethod.MakeGenericMethod(typeof(object)), - Expression.Constant(indexPath, typeof(string))); - } - - if (node.Method.Name == "get_Item" || IsAVObjectGet(node)) - { - var visitedObject = Visit(node.Object); - var indexer = visitedObject as MethodCallExpression; - if (IsAVObjectGet(indexer)) - { - var indexValue = GetValue(node.Arguments[0]) as string; - if (indexValue == null) - { - throw new InvalidOperationException("Index must be a string"); - } - var newPath = GetValue(indexer.Arguments[0]) + "." + indexValue; - return Expression.Call(indexer.Object, - getMethod.MakeGenericMethod(node.Type), - Expression.Constant(newPath, typeof(string))); - } - } - return base.VisitMethodCall(node); - } - } - - /// - /// Normalizes Where expressions. - /// - private class WhereNormalizer : ExpressionVisitor - { - - /// - /// Normalizes binary operators. <, >, <=, >= !=, and == - /// This puts the AVObject.Get() on the left side of the operation - /// (reversing it if necessary), and normalizes the AVObject.Get() - /// - protected override Expression VisitBinary(BinaryExpression node) - { - var leftTransformed = new ObjectNormalizer().Visit(node.Left) as MethodCallExpression; - var rightTransformed = new ObjectNormalizer().Visit(node.Right) as MethodCallExpression; - - MethodCallExpression objectExpression; - Expression filterExpression; - bool inverted; - if (leftTransformed != null) - { - objectExpression = leftTransformed; - filterExpression = node.Right; - inverted = false; - } - else - { - objectExpression = rightTransformed; - filterExpression = node.Left; - inverted = true; - } - - try - { - switch (node.NodeType) - { - case ExpressionType.GreaterThan: - if (inverted) - { - return Expression.LessThan(objectExpression, filterExpression); - } - else - { - return Expression.GreaterThan(objectExpression, filterExpression); - } - case ExpressionType.GreaterThanOrEqual: - if (inverted) - { - return Expression.LessThanOrEqual(objectExpression, filterExpression); - } - else - { - return Expression.GreaterThanOrEqual(objectExpression, filterExpression); - } - case ExpressionType.LessThan: - if (inverted) - { - return Expression.GreaterThan(objectExpression, filterExpression); - } - else - { - return Expression.LessThan(objectExpression, filterExpression); - } - case ExpressionType.LessThanOrEqual: - if (inverted) - { - return Expression.GreaterThanOrEqual(objectExpression, filterExpression); - } - else - { - return Expression.LessThanOrEqual(objectExpression, filterExpression); - } - case ExpressionType.Equal: - return Expression.Equal(objectExpression, filterExpression); - case ExpressionType.NotEqual: - return Expression.NotEqual(objectExpression, filterExpression); - } - } - catch (ArgumentException) - { - throw new InvalidOperationException("Operation not supported: " + node); - } - return base.VisitBinary(node); - } - - /// - /// If a ! operator is used, this removes the ! and instead calls the equivalent - /// function (so e.g. == becomes !=, < becomes >=, Contains becomes NotContains) - /// - protected override Expression VisitUnary(UnaryExpression node) - { - // Normalizes inversion - if (node.NodeType == ExpressionType.Not) - { - var visitedOperand = Visit(node.Operand); - var binaryOperand = visitedOperand as BinaryExpression; - if (binaryOperand != null) - { - switch (binaryOperand.NodeType) - { - case ExpressionType.GreaterThan: - return Expression.LessThanOrEqual(binaryOperand.Left, binaryOperand.Right); - case ExpressionType.GreaterThanOrEqual: - return Expression.LessThan(binaryOperand.Left, binaryOperand.Right); - case ExpressionType.LessThan: - return Expression.GreaterThanOrEqual(binaryOperand.Left, binaryOperand.Right); - case ExpressionType.LessThanOrEqual: - return Expression.GreaterThan(binaryOperand.Left, binaryOperand.Right); - case ExpressionType.Equal: - return Expression.NotEqual(binaryOperand.Left, binaryOperand.Right); - case ExpressionType.NotEqual: - return Expression.Equal(binaryOperand.Left, binaryOperand.Right); - } - } - - var methodCallOperand = visitedOperand as MethodCallExpression; - if (methodCallOperand != null) - { - if (methodCallOperand.Method.IsGenericMethod) - { - if (methodCallOperand.Method.GetGenericMethodDefinition() == containsMethod) - { - var genericNotContains = notContainsMethod.MakeGenericMethod( - methodCallOperand.Method.GetGenericArguments()); - return Expression.Call(genericNotContains, methodCallOperand.Arguments.ToArray()); - } - if (methodCallOperand.Method.GetGenericMethodDefinition() == notContainsMethod) - { - var genericContains = containsMethod.MakeGenericMethod( - methodCallOperand.Method.GetGenericArguments()); - return Expression.Call(genericContains, methodCallOperand.Arguments.ToArray()); - } - } - if (methodCallOperand.Method == containsKeyMethod) - { - return Expression.Call(notContainsKeyMethod, methodCallOperand.Arguments.ToArray()); - } - if (methodCallOperand.Method == notContainsKeyMethod) - { - return Expression.Call(containsKeyMethod, methodCallOperand.Arguments.ToArray()); - } - } - } - return base.VisitUnary(node); - } - - /// - /// Normalizes .Equals into == and Contains() into the appropriate stub. - /// - protected override Expression VisitMethodCall(MethodCallExpression node) - { - // Convert .Equals() into == - if (node.Method.Name == "Equals" && - node.Method.ReturnType == typeof(bool) && - node.Method.GetParameters().Length == 1) - { - var obj = new ObjectNormalizer().Visit(node.Object) as MethodCallExpression; - var parameter = new ObjectNormalizer().Visit(node.Arguments[0]) as MethodCallExpression; - if ((IsAVObjectGet(obj) && (obj.Object is ParameterExpression)) || - (IsAVObjectGet(parameter) && (parameter.Object is ParameterExpression))) - { - return Expression.Equal(node.Object, node.Arguments[0]); - } - } - - // Convert the .Contains() into a ContainsStub - if (node.Method != stringContains && - node.Method.Name == "Contains" && - node.Method.ReturnType == typeof(bool) && - node.Method.GetParameters().Length <= 2) - { - var collection = node.Method.GetParameters().Length == 1 ? - node.Object : - node.Arguments[0]; - var parameterIndex = node.Method.GetParameters().Length - 1; - var parameter = new ObjectNormalizer().Visit(node.Arguments[parameterIndex]) - as MethodCallExpression; - if (IsAVObjectGet(parameter) && (parameter.Object is ParameterExpression)) - { - var genericContains = containsMethod.MakeGenericMethod(parameter.Type); - return Expression.Call(genericContains, collection, parameter); - } - var target = new ObjectNormalizer().Visit(collection) as MethodCallExpression; - var element = node.Arguments[parameterIndex]; - if (IsAVObjectGet(target) && (target.Object is ParameterExpression)) - { - var genericContains = containsMethod.MakeGenericMethod(element.Type); - return Expression.Call(genericContains, target, element); - } - } - - // Convert obj["foo.bar"].ContainsKey("baz") into obj.ContainsKey("foo.bar.baz") - if (node.Method.Name == "ContainsKey" && - node.Method.ReturnType == typeof(bool) && - node.Method.GetParameters().Length == 1) - { - var getter = new ObjectNormalizer().Visit(node.Object) as MethodCallExpression; - Expression target = null; - string path = null; - if (IsAVObjectGet(getter) && getter.Object is ParameterExpression) - { - target = getter.Object; - path = GetValue(getter.Arguments[0]) + "." + GetValue(node.Arguments[0]); - return Expression.Call(containsKeyMethod, target, Expression.Constant(path)); - } - else if (node.Object is ParameterExpression) - { - target = node.Object; - path = GetValue(node.Arguments[0]) as string; - } - if (target != null && path != null) - { - return Expression.Call(containsKeyMethod, target, Expression.Constant(path)); - } - } - return base.VisitMethodCall(node); - } - } - - /// - /// Converts a normalized method call expression into the appropriate AVQuery clause. - /// - private static AVQuery WhereMethodCall( - this AVQuery source, Expression> expression, MethodCallExpression node) - where T : AVObject - { - if (IsAVObjectGet(node) && (node.Type == typeof(bool) || node.Type == typeof(bool?))) - { - // This is a raw boolean field access like 'where obj.Get("foo")' - return source.WhereEqualTo(GetValue(node.Arguments[0]) as string, true); - } - - MethodInfo translatedMethod; - if (functionMappings.TryGetValue(node.Method, out translatedMethod)) - { - var objTransformed = new ObjectNormalizer().Visit(node.Object) as MethodCallExpression; - if (!(IsAVObjectGet(objTransformed) && - objTransformed.Object == expression.Parameters[0])) - { - throw new InvalidOperationException( - "The left-hand side of a supported function call must be a AVObject field access."); - } - var fieldPath = GetValue(objTransformed.Arguments[0]); - var containedIn = GetValue(node.Arguments[0]); - var queryType = translatedMethod.DeclaringType.GetGenericTypeDefinition() - .MakeGenericType(typeof(T)); - translatedMethod = ReflectionHelpers.GetMethod(queryType, - translatedMethod.Name, - translatedMethod.GetParameters().Select(p => p.ParameterType).ToArray()); - return translatedMethod.Invoke(source, new[] { fieldPath, containedIn }) as AVQuery; - } - - if (node.Arguments[0] == expression.Parameters[0]) - { - // obj.ContainsKey("foo") --> query.WhereExists("foo") - if (node.Method == containsKeyMethod) - { - return source.WhereExists(GetValue(node.Arguments[1]) as string); - } - // !obj.ContainsKey("foo") --> query.WhereDoesNotExist("foo") - if (node.Method == notContainsKeyMethod) - { - return source.WhereDoesNotExist(GetValue(node.Arguments[1]) as string); - } - } - - if (node.Method.IsGenericMethod) - { - if (node.Method.GetGenericMethodDefinition() == containsMethod) - { - // obj.Get>("path").Contains(someValue) - if (IsAVObjectGet(node.Arguments[0] as MethodCallExpression)) - { - return source.WhereEqualTo( - GetValue(((MethodCallExpression)node.Arguments[0]).Arguments[0]) as string, - GetValue(node.Arguments[1])); - } - // someList.Contains(obj.Get("path")) - if (IsAVObjectGet(node.Arguments[1] as MethodCallExpression)) - { - var collection = GetValue(node.Arguments[0]) as System.Collections.IEnumerable; - return source.WhereContainedIn( - GetValue(((MethodCallExpression)node.Arguments[1]).Arguments[0]) as string, - collection.Cast()); - } - } - - if (node.Method.GetGenericMethodDefinition() == notContainsMethod) - { - // !obj.Get>("path").Contains(someValue) - if (IsAVObjectGet(node.Arguments[0] as MethodCallExpression)) - { - return source.WhereNotEqualTo( - GetValue(((MethodCallExpression)node.Arguments[0]).Arguments[0]) as string, - GetValue(node.Arguments[1])); - } - // !someList.Contains(obj.Get("path")) - if (IsAVObjectGet(node.Arguments[1] as MethodCallExpression)) - { - var collection = GetValue(node.Arguments[0]) as System.Collections.IEnumerable; - return source.WhereNotContainedIn( - GetValue(((MethodCallExpression)node.Arguments[1]).Arguments[0]) as string, - collection.Cast()); - } - } - } - throw new InvalidOperationException(node.Method + " is not a supported method call in a where expression."); - } - - /// - /// Converts a normalized binary expression into the appropriate AVQuery clause. - /// - private static AVQuery WhereBinaryExpression( - this AVQuery source, Expression> expression, BinaryExpression node) - where T : AVObject - { - var leftTransformed = new ObjectNormalizer().Visit(node.Left) as MethodCallExpression; - - if (!(IsAVObjectGet(leftTransformed) && - leftTransformed.Object == expression.Parameters[0])) - { - throw new InvalidOperationException( - "Where expressions must have one side be a field operation on a AVObject."); - } - - var fieldPath = GetValue(leftTransformed.Arguments[0]) as string; - var filterValue = GetValue(node.Right); - - if (filterValue != null && !AVEncoder.IsValidType(filterValue)) - { - throw new InvalidOperationException( - "Where clauses must use types compatible with AVObjects."); - } - - switch (node.NodeType) - { - case ExpressionType.GreaterThan: - return source.WhereGreaterThan(fieldPath, filterValue); - case ExpressionType.GreaterThanOrEqual: - return source.WhereGreaterThanOrEqualTo(fieldPath, filterValue); - case ExpressionType.LessThan: - return source.WhereLessThan(fieldPath, filterValue); - case ExpressionType.LessThanOrEqual: - return source.WhereLessThanOrEqualTo(fieldPath, filterValue); - case ExpressionType.Equal: - return source.WhereEqualTo(fieldPath, filterValue); - case ExpressionType.NotEqual: - return source.WhereNotEqualTo(fieldPath, filterValue); - default: - throw new InvalidOperationException( - "Where expressions do not support this operator."); - } - } - - /// - /// Filters a query based upon the predicate provided. - /// - /// The type of AVObject being queried for. - /// The base to which - /// the predicate will be added. - /// A function to test each AVObject for a condition. - /// The predicate must be able to be represented by one of the standard Where - /// functions on AVQuery - /// A new AVQuery whose results will match the given predicate as - /// well as the Source's filters. - public static AVQuery Where( - this AVQuery source, Expression> predicate) - where TSource : AVObject - { - // Handle top-level logic operators && and || - var binaryExpression = predicate.Body as BinaryExpression; - if (binaryExpression != null) - { - if (binaryExpression.NodeType == ExpressionType.AndAlso) - { - return source - .Where(Expression.Lambda>( - binaryExpression.Left, predicate.Parameters)) - .Where(Expression.Lambda>( - binaryExpression.Right, predicate.Parameters)); - } - if (binaryExpression.NodeType == ExpressionType.OrElse) - { - var left = source.Where(Expression.Lambda>( - binaryExpression.Left, predicate.Parameters)); - var right = source.Where(Expression.Lambda>( - binaryExpression.Right, predicate.Parameters)); - return left.Or(right); - } - } - - var normalized = new WhereNormalizer().Visit(predicate.Body); - - var methodCallExpr = normalized as MethodCallExpression; - if (methodCallExpr != null) - { - return source.WhereMethodCall(predicate, methodCallExpr); - } - - var binaryExpr = normalized as BinaryExpression; - if (binaryExpr != null) - { - return source.WhereBinaryExpression(predicate, binaryExpr); - } - - var unaryExpr = normalized as UnaryExpression; - if (unaryExpr != null && unaryExpr.NodeType == ExpressionType.Not) - { - var node = unaryExpr.Operand as MethodCallExpression; - if (IsAVObjectGet(node) && (node.Type == typeof(bool) || node.Type == typeof(bool?))) - { - // This is a raw boolean field access like 'where !obj.Get("foo")' - return source.WhereNotEqualTo(GetValue(node.Arguments[0]) as string, true); - } - } - - throw new InvalidOperationException( - "Encountered an unsupported expression for AVQueries."); - } - - /// - /// Normalizes an OrderBy's keySelector expression and then extracts the path - /// from the AVObject.Get() call. - /// - private static string GetOrderByPath( - Expression> keySelector) - { - string result = null; - var normalized = new ObjectNormalizer().Visit(keySelector.Body); - var callExpr = normalized as MethodCallExpression; - if (IsAVObjectGet(callExpr) && callExpr.Object == keySelector.Parameters[0]) - { - // We're operating on the parameter - result = GetValue(callExpr.Arguments[0]) as string; - } - if (result == null) - { - throw new InvalidOperationException( - "OrderBy expression must be a field access on a AVObject."); - } - return result; - } - - /// - /// Orders a query based upon the key selector provided. - /// - /// The type of AVObject being queried for. - /// The type of key returned by keySelector. - /// The query to order. - /// A function to extract a key from the AVObject. - /// A new AVQuery based on Source whose results will be ordered by - /// the key specified in the keySelector. - public static AVQuery OrderBy( - this AVQuery source, Expression> keySelector) - where TSource : AVObject - { - return source.OrderBy(GetOrderByPath(keySelector)); - } - - /// - /// Orders a query based upon the key selector provided. - /// - /// The type of AVObject being queried for. - /// The type of key returned by keySelector. - /// The query to order. - /// A function to extract a key from the AVObject. - /// A new AVQuery based on Source whose results will be ordered by - /// the key specified in the keySelector. - public static AVQuery OrderByDescending( - this AVQuery source, Expression> keySelector) - where TSource : AVObject - { - return source.OrderByDescending(GetOrderByPath(keySelector)); - } - - /// - /// Performs a subsequent ordering of a query based upon the key selector provided. - /// - /// The type of AVObject being queried for. - /// The type of key returned by keySelector. - /// The query to order. - /// A function to extract a key from the AVObject. - /// A new AVQuery based on Source whose results will be ordered by - /// the key specified in the keySelector. - public static AVQuery ThenBy( - this AVQuery source, Expression> keySelector) - where TSource : AVObject - { - return source.ThenBy(GetOrderByPath(keySelector)); - } - - /// - /// Performs a subsequent ordering of a query based upon the key selector provided. - /// - /// The type of AVObject being queried for. - /// The type of key returned by keySelector. - /// The query to order. - /// A function to extract a key from the AVObject. - /// A new AVQuery based on Source whose results will be ordered by - /// the key specified in the keySelector. - public static AVQuery ThenByDescending( - this AVQuery source, Expression> keySelector) - where TSource : AVObject - { - return source.ThenByDescending(GetOrderByPath(keySelector)); - } - - /// - /// Correlates the elements of two queries based on matching keys. - /// - /// The type of AVObjects of the first query. - /// The type of AVObjects of the second query. - /// The type of the keys returned by the key selector - /// functions. - /// The type of the result. This must match either - /// TOuter or TInner - /// The first query to join. - /// The query to join to the first query. - /// A function to extract a join key from the results of - /// the first query. - /// A function to extract a join key from the results of - /// the second query. - /// A function to select either the outer or inner query - /// result to determine which query is the base query. - /// A new AVQuery with a WhereMatchesQuery or WhereMatchesKeyInQuery - /// clause based upon the query indicated in the . - public static AVQuery Join( - this AVQuery outer, - AVQuery inner, - Expression> outerKeySelector, - Expression> innerKeySelector, - Expression> resultSelector) - where TOuter : AVObject - where TInner : AVObject - where TResult : AVObject - { - // resultSelector must select either the inner object or the outer object. If it's the inner - // object, reverse the query. - if (resultSelector.Body == resultSelector.Parameters[1]) - { - // The inner object was selected. - return inner.Join( - outer, - innerKeySelector, - outerKeySelector, - (i, o) => i) as AVQuery; - } - if (resultSelector.Body != resultSelector.Parameters[0]) - { - throw new InvalidOperationException("Joins must select either the outer or inner object."); - } - - // Normalize both selectors - Expression outerNormalized = new ObjectNormalizer().Visit(outerKeySelector.Body); - Expression innerNormalized = new ObjectNormalizer().Visit(innerKeySelector.Body); - MethodCallExpression outerAsGet = outerNormalized as MethodCallExpression; - MethodCallExpression innerAsGet = innerNormalized as MethodCallExpression; - if (IsAVObjectGet(outerAsGet) && outerAsGet.Object == outerKeySelector.Parameters[0]) - { - var outerKey = GetValue(outerAsGet.Arguments[0]) as string; - - if (IsAVObjectGet(innerAsGet) && innerAsGet.Object == innerKeySelector.Parameters[0]) - { - // Both are key accesses, so treat this as a WhereMatchesKeyInQuery - var innerKey = GetValue(innerAsGet.Arguments[0]) as string; - return outer.WhereMatchesKeyInQuery(outerKey, innerKey, inner) as AVQuery; - } - - if (innerKeySelector.Body == innerKeySelector.Parameters[0]) - { - // The inner selector is on the result of the query itself, so treat this as a - // WhereMatchesQuery - return outer.WhereMatchesQuery(outerKey, inner) as AVQuery; - } - throw new InvalidOperationException( - "The key for the joined object must be a AVObject or a field access " + - "on the AVObject."); - } - - // TODO (hallucinogen): If we ever support "and" queries fully and/or support a "where this object - // matches some key in some other query" (as opposed to requiring a key on this query), we - // can add support for even more types of joins. - - throw new InvalidOperationException( - "The key for the selected object must be a field access on the AVObject."); - } - } -} diff --git a/Storage/Source/Public/AVRelation.cs b/Storage/Source/Public/AVRelation.cs deleted file mode 100644 index fa7f13e..0000000 --- a/Storage/Source/Public/AVRelation.cs +++ /dev/null @@ -1,175 +0,0 @@ -using LeanCloud.Storage.Internal; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using System.Text; - -namespace LeanCloud -{ - /// - /// A common base class for AVRelations. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public abstract class AVRelationBase : IJsonConvertible - { - private AVObject parent; - private string key; - private string targetClassName; - - internal AVRelationBase(AVObject parent, string key) - { - EnsureParentAndKey(parent, key); - } - - internal AVRelationBase(AVObject parent, string key, string targetClassName) - : this(parent, key) - { - this.targetClassName = targetClassName; - } - - internal static IObjectSubclassingController SubclassingController - { - get - { - return AVPlugins.Instance.SubclassingController; - } - } - - internal void EnsureParentAndKey(AVObject parent, string key) - { - this.parent = this.parent ?? parent; - this.key = this.key ?? key; - Debug.Assert(this.parent == parent, "Relation retrieved from two different objects"); - Debug.Assert(this.key == key, "Relation retrieved from two different keys"); - } - - internal void Add(AVObject obj) - { - var change = new AVRelationOperation(new[] { obj }, null); - parent.PerformOperation(key, change); - targetClassName = change.TargetClassName; - } - - internal void Remove(AVObject obj) - { - var change = new AVRelationOperation(null, new[] { obj }); - parent.PerformOperation(key, change); - targetClassName = change.TargetClassName; - } - - IDictionary IJsonConvertible.ToJSON() - { - return new Dictionary { - { "__type", "Relation"}, - { "className", targetClassName} - }; - } - - internal AVQuery GetQuery() where T : AVObject - { - if (targetClassName != null) - { - return new AVQuery(targetClassName) - .WhereRelatedTo(parent, key); - } - - return new AVQuery(parent.ClassName) - .RedirectClassName(key) - .WhereRelatedTo(parent, key); - } - - internal AVQuery GetReverseQuery(T target) where T : AVObject - { - if (target.ObjectId == null) - { - throw new ArgumentNullException("target.ObjectId", "can not query a relation without target ObjectId."); - } - - return new AVQuery(parent.ClassName).WhereEqualTo(key, target); - } - - internal string TargetClassName - { - get - { - return targetClassName; - } - set - { - targetClassName = value; - } - } - - /// - /// Produces the proper AVRelation<T> instance for the given classname. - /// - internal static AVRelationBase CreateRelation(AVObject parent, - string key, - string targetClassName) - { - var targetType = SubclassingController.GetType(targetClassName) ?? typeof(AVObject); - - Expression>> createRelationExpr = - () => CreateRelation(parent, key, targetClassName); - var createRelationMethod = - ((MethodCallExpression)createRelationExpr.Body) - .Method - .GetGenericMethodDefinition() - .MakeGenericMethod(targetType); - return (AVRelationBase)createRelationMethod.Invoke(null, new object[] { parent, key, targetClassName }); - } - - private static AVRelation CreateRelation(AVObject parent, string key, string targetClassName) - where T : AVObject - { - return new AVRelation(parent, key, targetClassName); - } - } - - /// - /// Provides access to all of the children of a many-to-many relationship. Each instance of - /// AVRelation is associated with a particular parent and key. - /// - /// The type of the child objects. - public sealed class AVRelation : AVRelationBase where T : AVObject - { - - internal AVRelation(AVObject parent, string key) : base(parent, key) { } - - internal AVRelation(AVObject parent, string key, string targetClassName) - : base(parent, key, targetClassName) { } - - /// - /// Adds an object to this relation. The object must already have been saved. - /// - /// The object to add. - public void Add(T obj) - { - base.Add(obj); - } - - /// - /// Removes an object from this relation. The object must already have been saved. - /// - /// The object to remove. - public void Remove(T obj) - { - base.Remove(obj); - } - - /// - /// Gets a query that can be used to query the objects in this relation. - /// - public AVQuery Query - { - get - { - return base.GetQuery(); - } - } - } -} diff --git a/Storage/Source/Public/AVRole.cs b/Storage/Source/Public/AVRole.cs deleted file mode 100644 index 848832b..0000000 --- a/Storage/Source/Public/AVRole.cs +++ /dev/null @@ -1,111 +0,0 @@ -using LeanCloud.Storage.Internal; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; - -namespace LeanCloud -{ - /// - /// Represents a Role on the LeanCloud server. AVRoles represent groupings - /// of s for the purposes of granting permissions (e.g. - /// specifying a for a . Roles - /// are specified by their sets of child users and child roles, all of which are granted - /// any permissions that the parent role has. - /// - /// Roles must have a name (that cannot be changed after creation of the role), - /// and must specify an ACL. - /// - [AVClassName("_Role")] - public class AVRole : AVObject - { - private static readonly Regex namePattern = new Regex("^[0-9a-zA-Z_\\- ]+$"); - - /// - /// Constructs a new AVRole. You must assign a name and ACL to the role. - /// - public AVRole() : base() { } - - /// - /// Constructs a new AVRole with the given name. - /// - /// The name of the role to create. - /// The ACL for this role. Roles must have an ACL. - public AVRole(string name, AVACL acl) - : this() - { - Name = name; - ACL = acl; - } - - /// - /// Gets the name of the role. - /// - [AVFieldName("name")] - public string Name - { - get { return GetProperty("Name"); } - set { SetProperty(value, "Name"); } - } - - /// - /// Gets the for the s that are - /// direct children of this role. These users are granted any privileges that - /// this role has been granted (e.g. read or write access through ACLs). You can - /// add or remove child users from the role through this relation. - /// - [AVFieldName("users")] - public AVRelation Users - { - get { return GetRelationProperty("Users"); } - } - - /// - /// Gets the for the s that are - /// direct children of this role. These roles' users are granted any privileges that - /// this role has been granted (e.g. read or write access through ACLs). You can - /// add or remove child roles from the role through this relation. - /// - [AVFieldName("roles")] - public AVRelation Roles - { - get { return GetRelationProperty("Roles"); } - } - - internal override void OnSettingValue(ref string key, ref object value) - { - base.OnSettingValue(ref key, ref value); - if (key == "name") - { - if (ObjectId != null) - { - throw new InvalidOperationException( - "A role's name can only be set before it has been saved."); - } - if (!(value is string)) - { - throw new ArgumentException("A role's name must be a string.", "value"); - } - if (!namePattern.IsMatch((string)value)) - { - throw new ArgumentException( - "A role's name can only contain alphanumeric characters, _, -, and spaces.", - "value"); - } - } - } - - /// - /// Gets a over the Role collection. - /// - public static AVQuery Query - { - get - { - return new AVQuery(); - } - } - } -} diff --git a/Storage/Source/Public/AVSession.cs b/Storage/Source/Public/AVSession.cs deleted file mode 100644 index e915b40..0000000 --- a/Storage/Source/Public/AVSession.cs +++ /dev/null @@ -1,111 +0,0 @@ -using LeanCloud.Storage.Internal; -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud -{ - /// - /// Represents a session of a user for a LeanCloud application. - /// - [AVClassName("_Session")] - public class AVSession : AVObject - { - private static readonly HashSet readOnlyKeys = new HashSet { - "sessionToken", "createdWith", "restricted", "user", "expiresAt", "installationId" - }; - - protected override bool IsKeyMutable(string key) - { - return !readOnlyKeys.Contains(key); - } - - /// - /// Gets the session token for a user, if they are logged in. - /// - [AVFieldName("sessionToken")] - public string SessionToken - { - get { return GetProperty(null, "SessionToken"); } - } - - /// - /// Constructs a for AVSession. - /// - public static AVQuery Query - { - get - { - return new AVQuery(); - } - } - - internal static IAVSessionController SessionController - { - get - { - return AVPlugins.Instance.SessionController; - } - } - - /// - /// Gets the current object related to the current user. - /// - public static Task GetCurrentSessionAsync() - { - return GetCurrentSessionAsync(CancellationToken.None); - } - - /// - /// Gets the current object related to the current user. - /// - /// The cancellation token - public static Task GetCurrentSessionAsync(CancellationToken cancellationToken) - { - return AVUser.GetCurrentUserAsync().OnSuccess(t1 => - { - AVUser user = t1.Result; - if (user == null) - { - return Task.FromResult((AVSession)null); - } - - string sessionToken = user.SessionToken; - if (sessionToken == null) - { - return Task.FromResult((AVSession)null); - } - - return SessionController.GetSessionAsync(sessionToken, cancellationToken).OnSuccess(t => - { - AVSession session = AVObject.FromState(t.Result, "_Session"); - return session; - }); - }).Unwrap(); - } - - internal static Task RevokeAsync(string sessionToken, CancellationToken cancellationToken) - { - if (sessionToken == null || !SessionController.IsRevocableSessionToken(sessionToken)) - { - return Task.FromResult(0); - } - return SessionController.RevokeAsync(sessionToken, cancellationToken); - } - - internal static Task UpgradeToRevocableSessionAsync(string sessionToken, CancellationToken cancellationToken) - { - if (sessionToken == null || SessionController.IsRevocableSessionToken(sessionToken)) - { - return Task.FromResult(sessionToken); - } - - return SessionController.UpgradeToRevocableSessionAsync(sessionToken, cancellationToken).OnSuccess(t => - { - AVSession session = AVObject.FromState(t.Result, "_Session"); - return session.SessionToken; - }); - } - } -} diff --git a/Storage/Source/Public/AVStatus.cs b/Storage/Source/Public/AVStatus.cs deleted file mode 100644 index 7a7bc65..0000000 --- a/Storage/Source/Public/AVStatus.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud -{ - /// - /// 事件流系统中的一条状态 - /// - [AVClassName("_Status")] - public class AVStatus : AVObject - { - private static readonly HashSet readOnlyKeys = new HashSet { - "messageId", "inboxType", "data","Source" - }; - - protected override bool IsKeyMutable(string key) - { - return !readOnlyKeys.Contains(key); - } - } -} diff --git a/Storage/Source/Public/AVUploadProgressEventArgs.cs b/Storage/Source/Public/AVUploadProgressEventArgs.cs deleted file mode 100644 index dfa3112..0000000 --- a/Storage/Source/Public/AVUploadProgressEventArgs.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace LeanCloud { - /// - /// Represents upload progress. - /// - public class AVUploadProgressEventArgs : EventArgs { - public AVUploadProgressEventArgs() { } - - /// - /// Gets the progress (a number between 0.0 and 1.0) of an upload. - /// - public double Progress { get; set; } - } -} diff --git a/Storage/Source/Public/AVUser.cs b/Storage/Source/Public/AVUser.cs deleted file mode 100644 index f6cae24..0000000 --- a/Storage/Source/Public/AVUser.cs +++ /dev/null @@ -1,1310 +0,0 @@ -using LeanCloud.Storage.Internal; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud { - /// - /// Represents a user for a LeanCloud application. - /// - [AVClassName("_User")] - public class AVUser : AVObject { - private static readonly IDictionary authProviders = - new Dictionary(); - - private static readonly HashSet readOnlyKeys = new HashSet { - "sessionToken", "isNew" - }; - - internal static IAVUserController UserController { - get { - return AVPlugins.Instance.UserController; - } - } - - internal static IAVCurrentUserController CurrentUserController { - get { - return AVPlugins.Instance.CurrentUserController; - } - } - - /// - /// Whether the AVUser has been authenticated on this device. Only an authenticated - /// AVUser can be saved and deleted. - /// - [Obsolete("This property is deprecated, please use IsAuthenticatedAsync instead.")] - public bool IsAuthenticated { - get { - lock (mutex) { - return SessionToken != null && - CurrentUser != null && - CurrentUser.ObjectId == ObjectId; - } - } - } - - /// - /// Whether the AVUser has been authenticated on this device, and the AVUser's session token is expired. - /// Only an authenticated AVUser can be saved and deleted. - /// - public Task IsAuthenticatedAsync() { - lock (mutex) { - if (SessionToken == null || CurrentUser == null || CurrentUser.ObjectId != ObjectId) { - return Task.FromResult(false); - } - } - var command = new AVCommand(String.Format("users/me?session_token={0}", CurrentSessionToken), - method: "GET", - data: null); - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t => { - return AVClient.IsSuccessStatusCode(t.Result.Item1); - }); - } - - /// - /// Refresh this user's session token, and current session token will be invalid. - /// - public Task RefreshSessionTokenAsync(CancellationToken cancellationToken) { - return UserController.RefreshSessionTokenAsync(ObjectId, SessionToken, cancellationToken).OnSuccess(t => { - var serverState = t.Result; - HandleSave(serverState); - }); - } - - /// - /// Removes a key from the object's data if it exists. - /// - /// The key to remove. - /// Cannot remove the username key. - public override void Remove(string key) { - if (key == "username") { - throw new ArgumentException("Cannot remove the username key."); - } - base.Remove(key); - } - - protected override bool IsKeyMutable(string key) { - return !readOnlyKeys.Contains(key); - } - - internal override void HandleSave(IObjectState serverState) { - base.HandleSave(serverState); - - SynchronizeAllAuthData(); - CleanupAuthData(); - - MutateState(mutableClone => { - mutableClone.ServerData.Remove("password"); - }); - } - - /// - /// authenticated token. - /// - public string SessionToken { - get { - if (State.ContainsKey("sessionToken")) { - return State["sessionToken"] as string; - } - return null; - } - } - - internal static string CurrentSessionToken { - get { - Task sessionTokenTask = GetCurrentSessionTokenAsync(); - sessionTokenTask.Wait(); - return sessionTokenTask.Result; - } - } - - internal static Task GetCurrentSessionTokenAsync(CancellationToken cancellationToken = default) { - return CurrentUserController.GetCurrentSessionTokenAsync(cancellationToken); - } - - internal Task SetSessionTokenAsync(string newSessionToken) { - return SetSessionTokenAsync(newSessionToken, CancellationToken.None); - } - - internal Task SetSessionTokenAsync(string newSessionToken, CancellationToken cancellationToken) { - MutateState(mutableClone => { - mutableClone.ServerData["sessionToken"] = newSessionToken; - }); - - return SaveCurrentUserAsync(this); - } - - /// - /// Gets or sets the username. - /// - [AVFieldName("username")] - public string Username { - get { return GetProperty(null, "Username"); } - set { SetProperty(value, "Username"); } - } - - /// - /// Sets the password. - /// - [AVFieldName("password")] - public string Password { - private get { return GetProperty(null, "Password"); } - set { SetProperty(value, "Password"); } - } - - /// - /// Sets the email address. - /// - [AVFieldName("email")] - public string Email { - get { return GetProperty(null, "Email"); } - set { SetProperty(value, "Email"); } - } - - /// - /// 用户手机号。 - /// - [AVFieldName("mobilePhoneNumber")] - public string MobilePhoneNumber { - get { - return GetProperty(null, "MobilePhoneNumber"); - } - set { - SetProperty(value, "MobilePhoneNumber"); - } - } - - /// - /// 用户手机号是否已经验证 - /// - /// true if mobile phone verified; otherwise, false. - [AVFieldName("mobilePhoneVerified")] - public bool MobilePhoneVerified { - get { - return GetProperty(false, "MobilePhoneVerified"); - } - set { - SetProperty(value, "MobilePhoneVerified"); - } - } - - /// - /// 判断用户是否为匿名用户 - /// - public bool IsAnonymous { - get { - bool rtn = false; - if (this.AuthData != null) { - rtn = this.AuthData.Keys.Contains("anonymous"); - } - return rtn; - } - } - - internal Task SignUpAsync(Task toAwait, CancellationToken cancellationToken) { - return this.Create(toAwait, cancellationToken).OnSuccess(_ => SaveCurrentUserAsync(this)).Unwrap(); - } - - /// - /// Signs up a new user. This will create a new AVUser on the server and will also persist the - /// session on disk so that you can access the user using . A username and - /// password must be set before calling SignUpAsync. - /// - public Task SignUpAsync() { - return SignUpAsync(CancellationToken.None); - } - - /// - /// Signs up a new user. This will create a new AVUser on the server and will also persist the - /// session on disk so that you can access the user using . A username and - /// password must be set before calling SignUpAsync. - /// - /// The cancellation token. - public Task SignUpAsync(CancellationToken cancellationToken) { - return taskQueue.Enqueue(toAwait => SignUpAsync(toAwait, cancellationToken), - cancellationToken); - } - - #region 事件流系统相关 API - - /// - /// 关注某个用户 - /// - /// 被关注的用户 - /// - public Task FollowAsync(string userObjectId) { - return this.FollowAsync(userObjectId, null); - } - - /// - /// 关注某个用户 - /// - /// 被关注的用户Id - /// 关注的时候附加属性 - /// - public Task FollowAsync(string userObjectId, IDictionary data) { - if (data != null) { - data = this.EncodeForSaving(data); - } - var command = new AVCommand(string.Format("users/{0}/friendship/{1}", this.ObjectId, userObjectId), - method: "POST", - sessionToken: CurrentSessionToken, - data: data); - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t => { - return AVClient.IsSuccessStatusCode(t.Result.Item1); - }); - } - - /// - /// 取关某一个用户 - /// - /// - /// - public Task UnfollowAsync(string userObjectId) { - var command = new AVCommand(string.Format("users/{0}/friendship/{1}", this.ObjectId, userObjectId), - method: "DELETE", - sessionToken: CurrentSessionToken, - data: null); - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t => { - return AVClient.IsSuccessStatusCode(t.Result.Item1); - }); - } - - /// - /// 获取当前用户的关注者的查询 - /// - /// - public AVQuery GetFollowerQuery() { - AVQuery query = new AVQuery(); - query.RelativeUri = string.Format("users/{0}/followers", this.ObjectId); - return query; - } - - /// - /// 获取当前用户所关注的用户的查询 - /// - /// - public AVQuery GetFolloweeQuery() { - AVQuery query = new AVQuery(); - query.RelativeUri = string.Format("users/{0}/followees", this.ObjectId); - return query; - } - - /// - /// 同时查询关注了当前用户的关注者和当前用户所关注的用户 - /// - /// - public AVQuery GetFollowersAndFolloweesQuery() { - AVQuery query = new AVQuery(); - query.RelativeUri = string.Format("users/{0}/followersAndFollowees", this.ObjectId); - return query; - } - - /// - /// 获取当前用户的关注者 - /// - /// - public Task> GetFollowersAsync() { - return this.GetFollowerQuery().FindAsync(); - } - - /// - /// 获取当前用户所关注的用户 - /// - /// - public Task> GetFolloweesAsync() { - return this.GetFolloweeQuery().FindAsync(); - } - - - //public Task SendStatusAsync() - //{ - - //} - - #endregion - - /// - /// Logs in a user with a username and password. On success, this saves the session to disk so you - /// can retrieve the currently logged in user using . - /// - /// The username to log in with. - /// The password to log in with. - /// The newly logged-in user. - public static Task LogInAsync(string username, string password) { - return LogInAsync(username, password, CancellationToken.None); - } - - /// - /// Logs in a user with a username and password. On success, this saves the session to disk so you - /// can retrieve the currently logged in user using . - /// - /// The username to log in with. - /// The password to log in with. - /// The cancellation token. - /// The newly logged-in user. - public static Task LogInAsync(string username, - string password, - CancellationToken cancellationToken) { - return UserController.LogInAsync(username, null, password, cancellationToken).OnSuccess(t => { - AVUser user = AVObject.FromState(t.Result, "_User"); - return SaveCurrentUserAsync(user).OnSuccess(_ => user); - }).Unwrap(); - } - - /// - /// Logs in a user with a username and password. On success, this saves the session to disk so you - /// can retrieve the currently logged in user using . - /// - /// The session token to authorize with - /// The user if authorization was successful - public static Task BecomeAsync(string sessionToken) { - return BecomeAsync(sessionToken, CancellationToken.None); - } - - /// - /// Logs in a user with a username and password. On success, this saves the session to disk so you - /// can retrieve the currently logged in user using . - /// - /// The session token to authorize with - /// The cancellation token. - /// The user if authorization was successful - public static Task BecomeAsync(string sessionToken, CancellationToken cancellationToken) { - return UserController.GetUserAsync(sessionToken, cancellationToken).OnSuccess(t => { - AVUser user = AVObject.FromState(t.Result, "_User"); - return SaveCurrentUserAsync(user).OnSuccess(_ => user); - }).Unwrap(); - } - - protected override Task SaveAsync(Task toAwait, CancellationToken cancellationToken) { - lock (mutex) { - if (ObjectId == null) { - throw new InvalidOperationException("You must call SignUpAsync before calling SaveAsync."); - } - return base.SaveAsync(toAwait, cancellationToken).OnSuccess(_ => { - if (!CurrentUserController.IsCurrent(this)) { - return Task.FromResult(0); - } - return SaveCurrentUserAsync(this); - }).Unwrap(); - } - } - - internal override Task FetchAsyncInternal(Task toAwait, IDictionary queryString, CancellationToken cancellationToken) { - return base.FetchAsyncInternal(toAwait, queryString, cancellationToken).OnSuccess(t => { - if (!CurrentUserController.IsCurrent(this)) { - return Task.FromResult(t.Result); - } - // If this is already the current user, refresh its state on disk. - return SaveCurrentUserAsync(this).OnSuccess(_ => t.Result); - }).Unwrap(); - } - - /// - /// Logs out the currently logged in user session. This will remove the session from disk, log out of - /// linked services, and future calls to will return null. - /// - /// - /// Typically, you should use , unless you are managing your own threading. - /// - public static void LogOut() { - // TODO (hallucinogen): this will without a doubt fail in Unity. But what else can we do? - LogOutAsync().Wait(); - } - - /// - /// Logs out the currently logged in user session. This will remove the session from disk, log out of - /// linked services, and future calls to will return null. - /// - /// - /// This is preferable to using , unless your code is already running from a - /// background thread. - /// - public static Task LogOutAsync() { - return LogOutAsync(CancellationToken.None); - } - - /// - /// Logs out the currently logged in user session. This will remove the session from disk, log out of - /// linked services, and future calls to will return null. - /// - /// This is preferable to using , unless your code is already running from a - /// background thread. - /// - public static Task LogOutAsync(CancellationToken cancellationToken) { - return GetCurrentUserAsync().OnSuccess(t => { - LogOutWithProviders(); - - AVUser user = t.Result; - if (user == null) { - return Task.FromResult(0); - } - - return user.taskQueue.Enqueue(toAwait => user.LogOutAsync(toAwait, cancellationToken), cancellationToken); - }).Unwrap(); - } - - internal Task LogOutAsync(Task toAwait, CancellationToken cancellationToken) { - string oldSessionToken = SessionToken; - if (oldSessionToken == null) { - return Task.FromResult(0); - } - - // Cleanup in-memory session. - MutateState(mutableClone => { - mutableClone.ServerData.Remove("sessionToken"); - }); - var revokeSessionTask = AVSession.RevokeAsync(oldSessionToken, cancellationToken); - return Task.WhenAll(revokeSessionTask, CurrentUserController.LogOutAsync(cancellationToken)); - } - - private static void LogOutWithProviders() { - foreach (var provider in authProviders.Values) { - provider.Deauthenticate(); - } - } - - /// - /// Gets the currently logged in AVUser with a valid session, either from memory or disk - /// if necessary. - /// - public static AVUser CurrentUser { - get { - var userTask = GetCurrentUserAsync(); - // TODO (hallucinogen): this will without a doubt fail in Unity. How should we fix it? - userTask.Wait(); - return userTask.Result; - } - } - - public static Task GetCurrentAsync() { - var userTask = GetCurrentUserAsync(); - return userTask; - } - - /// - /// Gets the currently logged in AVUser with a valid session, either from memory or disk - /// if necessary, asynchronously. - /// - public static Task GetCurrentUserAsync() { - return GetCurrentUserAsync(CancellationToken.None); - } - - /// - /// Gets the currently logged in AVUser with a valid session, either from memory or disk - /// if necessary, asynchronously. - /// - internal static Task GetCurrentUserAsync(CancellationToken cancellationToken) { - return CurrentUserController.GetAsync(cancellationToken); - } - - private static Task SaveCurrentUserAsync(AVUser user) { - return SaveCurrentUserAsync(user, CancellationToken.None); - } - - private static Task SaveCurrentUserAsync(AVUser user, CancellationToken cancellationToken) { - return CurrentUserController.SetAsync(user, cancellationToken); - } - - internal static void ClearInMemoryUser() { - CurrentUserController.ClearFromMemory(); - } - - /// - /// Constructs a for AVUsers. - /// - public static AVQuery Query { - get { - return new AVQuery(); - } - } - - #region Legacy / Revocable Session Tokens - - private static readonly object isRevocableSessionEnabledMutex = new object(); - private static bool isRevocableSessionEnabled; - - /// - /// Tells server to use revocable session on LogIn and SignUp, even when App's Settings - /// has "Require Revocable Session" turned off. Issues network request in background to - /// migrate the sessionToken on disk to revocable session. - /// - /// The Task that upgrades the session. - public static Task EnableRevocableSessionAsync() { - return EnableRevocableSessionAsync(CancellationToken.None); - } - - /// - /// Tells server to use revocable session on LogIn and SignUp, even when App's Settings - /// has "Require Revocable Session" turned off. Issues network request in background to - /// migrate the sessionToken on disk to revocable session. - /// - /// The Task that upgrades the session. - public static Task EnableRevocableSessionAsync(CancellationToken cancellationToken) { - lock (isRevocableSessionEnabledMutex) { - isRevocableSessionEnabled = true; - } - - return GetCurrentUserAsync(cancellationToken).OnSuccess(t => { - var user = t.Result; - return user.UpgradeToRevocableSessionAsync(cancellationToken); - }); - } - - internal static void DisableRevocableSession() { - lock (isRevocableSessionEnabledMutex) { - isRevocableSessionEnabled = false; - } - } - - internal static bool IsRevocableSessionEnabled { - get { - lock (isRevocableSessionEnabledMutex) { - return isRevocableSessionEnabled; - } - } - } - - internal Task UpgradeToRevocableSessionAsync() { - return UpgradeToRevocableSessionAsync(CancellationToken.None); - } - - public Task UpgradeToRevocableSessionAsync(CancellationToken cancellationToken) { - return taskQueue.Enqueue(toAwait => UpgradeToRevocableSessionAsync(toAwait, cancellationToken), - cancellationToken); - } - - internal Task UpgradeToRevocableSessionAsync(Task toAwait, CancellationToken cancellationToken) { - string sessionToken = SessionToken; - - return toAwait.OnSuccess(_ => { - return AVSession.UpgradeToRevocableSessionAsync(sessionToken, cancellationToken); - }).Unwrap().OnSuccess(t => { - return SetSessionTokenAsync(t.Result); - }).Unwrap(); - } - - #endregion - - /// - /// Requests a password reset email to be sent to the specified email address associated with the - /// user account. This email allows the user to securely reset their password on the LeanCloud site. - /// - /// The email address associated with the user that forgot their password. - public static Task RequestPasswordResetAsync(string email) { - return RequestPasswordResetAsync(email, CancellationToken.None); - } - - /// - /// Requests a password reset email to be sent to the specified email address associated with the - /// user account. This email allows the user to securely reset their password on the LeanCloud site. - /// - /// The email address associated with the user that forgot their password. - /// The cancellation token. - public static Task RequestPasswordResetAsync(string email, - CancellationToken cancellationToken) { - return UserController.RequestPasswordResetAsync(email, cancellationToken); - } - - /// - /// Updates current user's password. Need the user's old password, - /// - /// The password. - /// Old password. - /// New password. - /// Cancellation token. - public Task UpdatePassword(string oldPassword, string newPassword, CancellationToken cancellationToken) { - return UserController.UpdatePasswordAsync(ObjectId, SessionToken, oldPassword, newPassword, cancellationToken); - } - - /// - /// Gets the authData for this user. - /// - internal IDictionary> AuthData { - get { - IDictionary> authData; - if (this.TryGetValue>>( - "authData", out authData)) { - return authData; - } - return null; - } - private set { - this["authData"] = value; - } - } - - private static IAVAuthenticationProvider GetProvider(string providerName) { - IAVAuthenticationProvider provider; - if (authProviders.TryGetValue(providerName, out provider)) { - return provider; - } - return null; - } - - /// - /// Removes null values from authData (which exist temporarily for unlinking) - /// - private void CleanupAuthData() { - lock (mutex) { - if (!CurrentUserController.IsCurrent(this)) { - return; - } - var authData = AuthData; - - if (authData == null) { - return; - } - - foreach (var pair in new Dictionary>(authData)) { - if (pair.Value == null) { - authData.Remove(pair.Key); - } - } - } - } - - /// - /// Synchronizes authData for all providers. - /// - private void SynchronizeAllAuthData() { - lock (mutex) { - var authData = AuthData; - - if (authData == null) { - return; - } - - foreach (var pair in authData) { - SynchronizeAuthData(GetProvider(pair.Key)); - } - } - } - - private void SynchronizeAuthData(IAVAuthenticationProvider provider) { - bool restorationSuccess = false; - lock (mutex) { - var authData = AuthData; - if (authData == null || provider == null) { - return; - } - IDictionary data; - if (authData.TryGetValue(provider.AuthType, out data)) { - restorationSuccess = provider.RestoreAuthentication(data); - } - } - - if (!restorationSuccess) { - this.UnlinkFromAsync(provider.AuthType, CancellationToken.None); - } - } - - public Task LinkWithAsync(string authType, IDictionary data, CancellationToken cancellationToken) { - return taskQueue.Enqueue(toAwait => { - AuthData = new Dictionary>(); - AuthData[authType] = data; - return SaveAsync(cancellationToken); - }, cancellationToken); - } - - public Task LinkWithAsync(string authType, CancellationToken cancellationToken) { - var provider = GetProvider(authType); - return provider.AuthenticateAsync(cancellationToken) - .OnSuccess(t => LinkWithAsync(authType, t.Result, cancellationToken)) - .Unwrap(); - } - - /// - /// Unlinks a user from a service. - /// - public Task UnlinkFromAsync(string authType, CancellationToken cancellationToken) { - return LinkWithAsync(authType, null, cancellationToken); - } - - /// - /// Checks whether a user is linked to a service. - /// - internal bool IsLinked(string authType) { - lock (mutex) { - return AuthData != null && AuthData.ContainsKey(authType) && AuthData[authType] != null; - } - } - - internal static Task LogInWithAsync(string authType, - IDictionary data, - bool failOnNotExist, - CancellationToken cancellationToken) { - AVUser user = null; - - return UserController.LogInAsync(authType, data, failOnNotExist, cancellationToken).OnSuccess(t => { - user = AVObject.FromState(t.Result, "_User"); - - lock (user.mutex) { - if (user.AuthData == null) { - user.AuthData = new Dictionary>(); - } - user.AuthData[authType] = data; - user.SynchronizeAllAuthData(); - } - - return SaveCurrentUserAsync(user); - }).Unwrap().OnSuccess(t => user); - } - - internal static Task LogInWithAsync(string authType, - CancellationToken cancellationToken) { - var provider = GetProvider(authType); - return provider.AuthenticateAsync(cancellationToken) - .OnSuccess(authData => LogInWithAsync(authType, authData.Result, false, cancellationToken)) - .Unwrap(); - } - - internal static void RegisterProvider(IAVAuthenticationProvider provider) { - authProviders[provider.AuthType] = provider; - if (CurrentUser != null) { - CurrentUser.SynchronizeAuthData(provider); - } - } - - #region 手机号登录 - - internal static Task LogInWithParametersAsync(Dictionary strs, CancellationToken cancellationToken) { - AVUser avUser = CreateWithoutData(null); - - return UserController.LogInWithParametersAsync("login", strs, cancellationToken).OnSuccess(t => { - var user = CreateWithoutData(null); - user.HandleFetchResult(t.Result); - return SaveCurrentUserAsync(user).OnSuccess(_ => user); - }).Unwrap(); - } - - /// - /// 以手机号和密码实现登陆。 - /// - /// 手机号 - /// 密码 - /// - public static Task LogInByMobilePhoneNumberAsync(string mobilePhoneNumber, string password) { - return LogInByMobilePhoneNumberAsync(mobilePhoneNumber, password, CancellationToken.None); - } - - /// - /// 用邮箱作和密码匹配登录 - /// - /// 邮箱 - /// 密码 - /// - public static Task LogInByEmailAsync(string email, string password, CancellationToken cancellationToken = default) { - return UserController.LogInAsync(null, email, password, cancellationToken).OnSuccess(t => { - AVUser user = FromState(t.Result, "_User"); - return SaveCurrentUserAsync(user).OnSuccess(_ => user); - }).Unwrap(); - } - - - /// - /// 以手机号和密码匹配登陆 - /// - /// 手机号 - /// 密码 - /// - /// - public static Task LogInByMobilePhoneNumberAsync(string mobilePhoneNumber, string password, CancellationToken cancellationToken) { - Dictionary strs = new Dictionary() - { - { "mobilePhoneNumber", mobilePhoneNumber }, - { "password", password } - }; - return LogInWithParametersAsync(strs, cancellationToken); - } - - /// - /// 以手机号和验证码登陆 - /// - /// 手机号 - /// 短信验证码 - /// - /// - public static Task LogInBySmsCodeAsync(string mobilePhoneNumber, string smsCode, CancellationToken cancellationToken = default) { - Dictionary strs = new Dictionary() - { - { "mobilePhoneNumber", mobilePhoneNumber }, - { "smsCode", smsCode } - }; - return LogInWithParametersAsync(strs, cancellationToken); - } - - /// - /// Requests the login SMS code asynchronous. - /// - /// The mobile phone number. - /// - public static Task RequestLogInSmsCodeAsync(string mobilePhoneNumber) { - return RequestLogInSmsCodeAsync(mobilePhoneNumber, CancellationToken.None); - } - - /// - /// Requests the login SMS code asynchronous. - /// - /// The mobile phone number. - /// Validate token. - /// - public static Task RequestLogInSmsCodeAsync(string mobilePhoneNumber, string validateToken) { - return RequestLogInSmsCodeAsync(mobilePhoneNumber, null, CancellationToken.None); - } - - /// - /// Requests the login SMS code asynchronous. - /// - /// The mobile phone number. - /// The cancellation token. - /// - public static Task RequestLogInSmsCodeAsync(string mobilePhoneNumber, CancellationToken cancellationToken) { - return RequestLogInSmsCodeAsync(mobilePhoneNumber, null, cancellationToken); - } - - /// - /// Requests the login SMS code asynchronous. - /// - /// The mobile phone number. - /// Validate token. - /// The cancellation token. - /// - public static Task RequestLogInSmsCodeAsync(string mobilePhoneNumber, string validateToken, CancellationToken cancellationToken) { - Dictionary strs = new Dictionary { - { "mobilePhoneNumber", mobilePhoneNumber }, - }; - if (string.IsNullOrEmpty(validateToken)) { - strs.Add("validate_token", validateToken); - } - var command = new AVCommand("requestLoginSmsCode", "POST", CurrentSessionToken, data: strs); - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t => { - return AVClient.IsSuccessStatusCode(t.Result.Item1); - }); - } - - /// - /// 手机号一键登录 - /// - /// 手机号 - /// 短信验证码 - /// - public static Task SignUpOrLogInByMobilePhoneAsync(string mobilePhoneNumber, string smsCode, CancellationToken cancellationToken) { - Dictionary strs = new Dictionary() - { - { "mobilePhoneNumber", mobilePhoneNumber }, - { "smsCode", smsCode } - }; - return UserController.LogInWithParametersAsync("usersByMobilePhone", strs, cancellationToken).OnSuccess(t => { - var user = CreateWithoutData(null); - user.HandleFetchResult(t.Result); - return SaveCurrentUserAsync(user).OnSuccess(_ => user); - }).Unwrap(); - } - - /// - /// 手机号一键登录 - /// - /// signup or login by mobile phone async. - /// 手机号 - /// 短信验证码 - public static Task SignUpOrLogInByMobilePhoneAsync(string mobilePhoneNumber, string smsCode) { - return SignUpOrLogInByMobilePhoneAsync(mobilePhoneNumber, smsCode, CancellationToken.None); - } - - #region mobile sms shortcode sign up & log in. - /// - /// Send sign up sms code async. - /// - /// The sign up sms code async. - /// Mobile phone number. - public static Task SendSignUpSmsCodeAsync(string mobilePhoneNumber) { - return AVCloud.RequestSMSCodeAsync(mobilePhoneNumber); - } - - /// - /// Sign up by mobile phone async. - /// - /// The up by mobile phone async. - /// Mobile phone number. - /// Sms code. - public static Task SignUpByMobilePhoneAsync(string mobilePhoneNumber, string smsCode) { - return SignUpOrLogInByMobilePhoneAsync(mobilePhoneNumber, smsCode); - } - - /// - /// Send log in sms code async. - /// - /// The log in sms code async. - /// Mobile phone number. - public static Task SendLogInSmsCodeAsync(string mobilePhoneNumber) { - return RequestLogInSmsCodeAsync(mobilePhoneNumber); - } - - /// - /// Log in by mobile phone async. - /// - /// The in by mobile phone async. - /// Mobile phone number. - /// Sms code. - public static Task LogInByMobilePhoneAsync(string mobilePhoneNumber, string smsCode) { - return LogInBySmsCodeAsync(mobilePhoneNumber, smsCode); - } - #endregion - #endregion - - #region 重置密码 - /// - /// 请求重置密码,需要传入注册时使用的手机号。 - /// - /// 注册时使用的手机号 - /// - public static Task RequestPasswordResetBySmsCode(string mobilePhoneNumber) { - return RequestPasswordResetBySmsCode(mobilePhoneNumber, null, CancellationToken.None); - } - - /// - /// 请求重置密码,需要传入注册时使用的手机号。 - /// - /// 注册时使用的手机号 - /// cancellationToken - /// - public static Task RequestPasswordResetBySmsCode(string mobilePhoneNumber, CancellationToken cancellationToken) { - return RequestPasswordResetBySmsCode(mobilePhoneNumber, null, cancellationToken); - } - - /// - /// 请求重置密码,需要传入注册时使用的手机号。 - /// - /// 注册时使用的手机号 - /// Validate token. - /// - public static Task RequestPasswordResetBySmsCode(string mobilePhoneNumber, string validateToken) { - return RequestPasswordResetBySmsCode(mobilePhoneNumber, validateToken, CancellationToken.None); - } - - /// - /// 请求重置密码,需要传入注册时使用的手机号。 - /// - /// 注册时使用的手机号 - /// Validate token. - /// cancellationToken - /// - public static Task RequestPasswordResetBySmsCode(string mobilePhoneNumber, string validateToken, CancellationToken cancellationToken) { - string currentSessionToken = AVUser.CurrentSessionToken; - Dictionary strs = new Dictionary() - { - { "mobilePhoneNumber", mobilePhoneNumber }, - }; - if (string.IsNullOrEmpty(validateToken)) { - strs.Add("validate_token", validateToken); - } - var command = new AVCommand("requestPasswordResetBySmsCode", "POST", currentSessionToken, data: strs); - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t => { - return AVClient.IsSuccessStatusCode(t.Result.Item1); - }); - } - - /// - /// 通过验证码重置密码。 - /// - /// 新密码 - /// 6位数验证码 - /// - public static Task ResetPasswordBySmsCodeAsync(string newPassword, string smsCode) { - return AVUser.ResetPasswordBySmsCodeAsync(newPassword, smsCode, CancellationToken.None); - } - - /// - /// 通过验证码重置密码。 - /// - /// 新密码 - /// 6位数验证码 - /// cancellationToken - /// - public static Task ResetPasswordBySmsCodeAsync(string newPassword, string smsCode, CancellationToken cancellationToken) { - string currentSessionToken = AVUser.CurrentSessionToken; - Dictionary strs = new Dictionary() - { - { "password", newPassword } - }; - var command = new AVCommand("resetPasswordBySmsCode/" + smsCode, "PUT", currentSessionToken, data: strs); - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t => { - return AVClient.IsSuccessStatusCode(t.Result.Item1); - }); - } - - /// - /// 发送认证码到需要认证的手机上 - /// - /// 手机号 - /// - public static Task RequestMobilePhoneVerifyAsync(string mobilePhoneNumber) { - return RequestMobilePhoneVerifyAsync(mobilePhoneNumber, null, CancellationToken.None); - } - - /// - /// 发送认证码到需要认证的手机上 - /// - /// 手机号 - /// Validate token. - /// - public static Task RequestMobilePhoneVerifyAsync(string mobilePhoneNumber, string validateToken) { - return RequestMobilePhoneVerifyAsync(mobilePhoneNumber, validateToken, CancellationToken.None); - } - - /// - /// 发送认证码到需要认证的手机上 - /// - /// 手机号 - /// CancellationToken - /// - public static Task RequestMobilePhoneVerifyAsync(string mobilePhoneNumber, CancellationToken cancellationToken) { - return RequestMobilePhoneVerifyAsync(mobilePhoneNumber, null, cancellationToken); - } - - /// - /// 发送认证码到需要认证的手机上 - /// - /// 手机号 - /// Validate token. - /// CancellationToken - /// - public static Task RequestMobilePhoneVerifyAsync(string mobilePhoneNumber, string validateToken, CancellationToken cancellationToken) { - string currentSessionToken = CurrentSessionToken; - Dictionary strs = new Dictionary() - { - { "mobilePhoneNumber", mobilePhoneNumber } - }; - if (string.IsNullOrEmpty(validateToken)) { - strs.Add("validate_token", validateToken); - } - var command = new AVCommand("requestMobilePhoneVerify", "POST", currentSessionToken, data: strs); - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t => { - return AVClient.IsSuccessStatusCode(t.Result.Item1); - }); - } - - /// - /// 验证手机验证码是否为有效值 - /// - /// 手机收到的验证码 - /// 手机号,可选 - /// - public static Task VerifyMobilePhoneAsync(string code, string mobilePhoneNumber) { - var command = new AVCommand("verifyMobilePhone/" + code.Trim() + "?mobilePhoneNumber=" + mobilePhoneNumber.Trim(), - "POST", - null, - data: null); - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t => { - return AVClient.IsSuccessStatusCode(t.Result.Item1); - }); - } - - /// - /// 验证手机验证码是否为有效值 - /// - /// 手机收到的验证码 - /// - public static Task VerifyMobilePhoneAsync(string code) { - var command = new AVCommand("verifyMobilePhone/" + code.Trim(), - "POST", - null, - data: null); - - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t => { - return AVClient.IsSuccessStatusCode(t.Result.Item1); - }); - } - - /// - /// 验证手机验证码是否为有效值 - /// - /// 手机收到的验证码 - /// cancellationToken - /// - public static Task VerifyMobilePhoneAsync(string code, CancellationToken cancellationToken) { - return VerifyMobilePhoneAsync(code, CancellationToken.None); - } - - #endregion - - #region 邮箱验证 - /// - /// 申请发送验证邮箱的邮件,一周之内有效 - /// 如果该邮箱已经验证通过,会直接返回 True,并不会真正发送邮件 - /// 注意,不能频繁的调用此接口,一天之内只允许向同一个邮箱发送验证邮件 3 次,超过调用次数,会直接返回错误 - /// - /// 邮箱地址 - /// - public static Task RequestEmailVerifyAsync(string email) { - Dictionary strs = new Dictionary { - { "email", email } - }; - var command = new AVCommand("requestEmailVerify", - "POST", - null, - data: strs); - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).ContinueWith(t => { - return AVClient.IsSuccessStatusCode(t.Result.Item1); - }); - } - #endregion - - #region in no-local-storage enviroment - - internal Task Create() { - return Create(CancellationToken.None); - } - - internal Task Create(CancellationToken cancellationToken) { - return taskQueue.Enqueue(toAwait => Create(toAwait, cancellationToken), - cancellationToken); - } - - internal Task Create(Task toAwait, CancellationToken cancellationToken) { - if (AuthData == null) { - // TODO (hallucinogen): make an Extension of Task to create Task with exception/canceled. - if (string.IsNullOrEmpty(Username)) { - TaskCompletionSource tcs = new TaskCompletionSource(); - tcs.TrySetException(new InvalidOperationException("Cannot sign up user with an empty name.")); - return tcs.Task; - } - if (string.IsNullOrEmpty(Password)) { - TaskCompletionSource tcs = new TaskCompletionSource(); - tcs.TrySetException(new InvalidOperationException("Cannot sign up user with an empty password.")); - return tcs.Task; - } - } - if (!string.IsNullOrEmpty(ObjectId)) { - TaskCompletionSource tcs = new TaskCompletionSource(); - tcs.TrySetException(new InvalidOperationException("Cannot sign up a user that already exists.")); - return tcs.Task; - } - - IDictionary currentOperations = StartSave(); - - return toAwait.OnSuccess(_ => { - return UserController.SignUpAsync(State, currentOperations, cancellationToken); - }).Unwrap().ContinueWith(t => { - if (t.IsFaulted || t.IsCanceled) { - HandleFailedSave(currentOperations); - } else { - var serverState = t.Result; - HandleSave(serverState); - } - return t; - }).Unwrap(); - } - #endregion - - - #region task session token for http request - internal static Task TakeSessionToken(string sesstionToken = null) { - var sessionTokenTask = Task.FromResult(sesstionToken); - if (sesstionToken == null) - sessionTokenTask = AVUser.GetCurrentAsync().OnSuccess(u => { - if (u.Result != null) - return u.Result.SessionToken; - return null; - }); - return sessionTokenTask; - } - #endregion - - - #region AVUser Extension - public IDictionary> GetAuthData() { - return AuthData; - } - - /// - /// use 3rd auth data to sign up or log in.if user with the same auth data exits,it will transfer as log in. - /// - /// OAuth data, like {"accessToken":"xxxxxx"} - /// auth platform,maybe "facebook"/"twiiter"/"weibo"/"weixin" .etc - /// - /// - public static Task LogInWithAuthDataAsync(IDictionary data, - string platform, - AVUserAuthDataLogInOption options = null, - CancellationToken cancellationToken = default) { - if (options == null) { - options = new AVUserAuthDataLogInOption(); - } - return LogInWithAsync(platform, data, options.FailOnNotExist, cancellationToken); - } - - public static Task LogInWithAuthDataAndUnionIdAsync( - IDictionary authData, - string platform, - string unionId, - AVUserAuthDataLogInOption options = null, - CancellationToken cancellationToken = default) { - if (options == null) { - options = new AVUserAuthDataLogInOption(); - } - MergeAuthData(authData, unionId, options); - return LogInWithAsync(platform, authData, options.FailOnNotExist, cancellationToken); - } - - public static Task LogInAnonymouslyAsync(CancellationToken cancellationToken = default) { - var data = new Dictionary { - { "id", Guid.NewGuid().ToString() } - }; - var options = new AVUserAuthDataLogInOption(); - return LogInWithAuthDataAsync(data, "anonymous", options, cancellationToken); - } - - [Obsolete("please use LogInWithAuthDataAsync instead.")] - public static Task LogInWithAsync(string authType, IDictionary data, CancellationToken cancellationToken) { - return LogInWithAsync(authType, data, false, cancellationToken); - } - - /// - /// link a 3rd auth account to the user. - /// - /// OAuth data, like {"accessToken":"xxxxxx"} - /// auth platform,maybe "facebook"/"twiiter"/"weibo"/"weixin" .etc - /// - /// - public Task AssociateAuthDataAsync(IDictionary data, string platform, CancellationToken cancellationToken = default) { - return LinkWithAsync(platform, data, cancellationToken); - } - - public Task AssociateAuthDataAndUnionIdAsync( - IDictionary authData, - string platform, - string unionId, - AVUserAuthDataLogInOption options = null, - CancellationToken cancellationToken = default) { - if (options == null) { - options = new AVUserAuthDataLogInOption(); - } - MergeAuthData(authData, unionId, options); - return LinkWithAsync(platform, authData, cancellationToken); - } - - /// - /// unlink a 3rd auth account from the user. - /// - /// auth platform,maybe "facebook"/"twiiter"/"weibo"/"weixin" .etc - /// - /// - public Task DisassociateWithAuthDataAsync(string platform, CancellationToken cancellationToken = default) { - return UnlinkFromAsync(platform, cancellationToken); - } - - /// 合并为支持 AuthData 的格式 - static void MergeAuthData(IDictionary authData, string unionId, AVUserAuthDataLogInOption options) { - authData["platform"] = options.UnionIdPlatform; - authData["main_account"] = options.AsMainAccount; - authData["unionid"] = unionId; - } - #endregion - } -} diff --git a/Storage/Source/Public/AVUserAuthDataLogInOption.cs b/Storage/Source/Public/AVUserAuthDataLogInOption.cs deleted file mode 100644 index 1918c2b..0000000 --- a/Storage/Source/Public/AVUserAuthDataLogInOption.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; - -namespace LeanCloud { - /// - /// AuthData 登陆选项 - /// - public class AVUserAuthDataLogInOption { - - /// - /// unionId platform - /// - /// unionId platform. - public string UnionIdPlatform; - - /// - /// If true, the unionId will be associated with the user. - /// - /// true If true, the unionId will be associated with the user. false. - public bool AsMainAccount; - - /// - /// If true, the login request will fail when no user matches this authData exists. - /// - /// true If true, the login request will fail when no user matches this authData exists. false. - public bool FailOnNotExist; - - public AVUserAuthDataLogInOption() { - UnionIdPlatform = "weixin"; - AsMainAccount = false; - FailOnNotExist = false; - } - } -} diff --git a/Storage/Source/Public/IAVQuery.cs b/Storage/Source/Public/IAVQuery.cs deleted file mode 100644 index 7e883d0..0000000 --- a/Storage/Source/Public/IAVQuery.cs +++ /dev/null @@ -1,2068 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Text.RegularExpressions; -using System.Threading; -using System.Threading.Tasks; -using LeanCloud.Storage.Internal; - -namespace LeanCloud -{ - /// - /// Query 对象的基础接口 - /// - public interface IAVQuery - { - - } - - /// - /// LeanCloud 存储对象的接触接口 - /// - public interface IAVObject - { - - } - - public abstract class AVQueryBase : IAVQuery - where T : IAVObject - { - internal string className; - internal Dictionary where; - internal ReadOnlyCollection orderBy; - internal ReadOnlyCollection includes; - internal ReadOnlyCollection selectedKeys; - internal String redirectClassNameForKey; - internal int? skip; - internal int? limit; - - /// - /// 构建查询字符串 - /// - /// 是否包含 ClassName - /// - public IDictionary BuildParameters(bool includeClassName = false) - { - Dictionary result = new Dictionary(); - if (where != null) - { - result["where"] = PointerOrLocalIdEncoder.Instance.Encode(where); - } - if (orderBy != null) - { - result["order"] = string.Join(",", orderBy.ToArray()); - } - if (skip != null) - { - result["skip"] = skip.Value; - } - if (limit != null) - { - result["limit"] = limit.Value; - } - if (includes != null) - { - result["include"] = string.Join(",", includes.ToArray()); - } - if (selectedKeys != null) - { - result["keys"] = string.Join(",", selectedKeys.ToArray()); - } - if (includeClassName) - { - result["className"] = className; - } - if (redirectClassNameForKey != null) - { - result["redirectClassNameForKey"] = redirectClassNameForKey; - } - return result; - } - - public virtual Dictionary Where - { - get - { - return this.where; - } - set - { - this.where = value; - } - } - - public virtual IDictionary MergeWhere(IDictionary primary, IDictionary secondary) - { - if (secondary == null) - { - return primary; - } - var newWhere = new Dictionary(primary); - foreach (var pair in secondary) - { - var condition = pair.Value as IDictionary; - if (newWhere.ContainsKey(pair.Key)) - { - var oldCondition = newWhere[pair.Key] as IDictionary; - if (oldCondition == null || condition == null) - { - throw new ArgumentException("More than one where clause for the given key provided."); - } - var newCondition = new Dictionary(oldCondition); - foreach (var conditionPair in condition) - { - if (newCondition.ContainsKey(conditionPair.Key)) - { - throw new ArgumentException("More than one condition for the given key provided."); - } - newCondition[conditionPair.Key] = conditionPair.Value; - } - newWhere[pair.Key] = newCondition; - } - else - { - newWhere[pair.Key] = pair.Value; - } - } - return newWhere; - } - } - - public abstract class AVQueryPair - where S : IAVQuery - where T : IAVObject - - { - protected readonly string className; - protected readonly Dictionary where; - protected readonly ReadOnlyCollection orderBy; - protected readonly ReadOnlyCollection includes; - protected readonly ReadOnlyCollection selectedKeys; - protected readonly string redirectClassNameForKey; - protected readonly int? skip; - protected readonly int? limit; - - internal string ClassName { get { return className; } } - - private string relativeUri; - internal string RelativeUri - { - get - { - string rtn = string.Empty; - if (string.IsNullOrEmpty(relativeUri)) - { - rtn = "classes/" + Uri.EscapeDataString(this.className); - } - else - { - rtn = relativeUri; - } - return rtn; - } - set - { - relativeUri = value; - } - } - public Dictionary Condition - { - get { return this.where; } - } - - protected AVQueryPair() - { - - } - - public abstract S CreateInstance(IDictionary where = null, - IEnumerable replacementOrderBy = null, - IEnumerable thenBy = null, - int? skip = null, - int? limit = null, - IEnumerable includes = null, - IEnumerable selectedKeys = null, - string redirectClassNameForKey = null); - - /// - /// Private constructor for composition of queries. A Source query is required, - /// but the remaining values can be null if they won't be changed in this - /// composition. - /// - protected AVQueryPair(AVQueryPair source, - IDictionary where = null, - IEnumerable replacementOrderBy = null, - IEnumerable thenBy = null, - int? skip = null, - int? limit = null, - IEnumerable includes = null, - IEnumerable selectedKeys = null, - string redirectClassNameForKey = null) - { - if (source == null) - { - throw new ArgumentNullException("Source"); - } - - className = source.className; - this.where = source.where; - this.orderBy = source.orderBy; - this.skip = source.skip; - this.limit = source.limit; - this.includes = source.includes; - this.selectedKeys = source.selectedKeys; - this.redirectClassNameForKey = source.redirectClassNameForKey; - - if (where != null) - { - var newWhere = MergeWhereClauses(where); - this.where = new Dictionary(newWhere); - } - - if (replacementOrderBy != null) - { - this.orderBy = new ReadOnlyCollection(replacementOrderBy.ToList()); - } - - if (thenBy != null) - { - if (this.orderBy == null) - { - throw new ArgumentException("You must call OrderBy before calling ThenBy."); - } - var newOrderBy = new List(this.orderBy); - newOrderBy.AddRange(thenBy); - this.orderBy = new ReadOnlyCollection(newOrderBy); - } - - // Remove duplicates. - if (this.orderBy != null) - { - var newOrderBy = new HashSet(this.orderBy); - this.orderBy = new ReadOnlyCollection(newOrderBy.ToList()); - } - - if (skip != null) - { - this.skip = (this.skip ?? 0) + skip; - } - - if (limit != null) - { - this.limit = limit; - } - - if (includes != null) - { - var newIncludes = MergeIncludes(includes); - this.includes = new ReadOnlyCollection(newIncludes.ToList()); - } - - if (selectedKeys != null) - { - var newSelectedKeys = MergeSelectedKeys(selectedKeys); - this.selectedKeys = new ReadOnlyCollection(newSelectedKeys.ToList()); - } - - if (redirectClassNameForKey != null) - { - this.redirectClassNameForKey = redirectClassNameForKey; - } - } - - public AVQueryPair(string className) - { - if (string.IsNullOrEmpty(className)) - { - throw new ArgumentNullException("className", "Must specify a AVObject class name when creating a AVQuery."); - } - this.className = className; - } - - private HashSet MergeIncludes(IEnumerable includes) - { - if (this.includes == null) - { - return new HashSet(includes); - } - var newIncludes = new HashSet(this.includes); - foreach (var item in includes) - { - newIncludes.Add(item); - } - return newIncludes; - } - - private HashSet MergeSelectedKeys(IEnumerable selectedKeys) - { - if (this.selectedKeys == null) - { - return new HashSet(selectedKeys); - } - var newSelectedKeys = new HashSet(this.selectedKeys); - foreach (var item in selectedKeys) - { - newSelectedKeys.Add(item); - } - return newSelectedKeys; - } - - private IDictionary MergeWhereClauses(IDictionary where) - { - return MergeWhere(this.where, where); - } - - public virtual IDictionary MergeWhere(IDictionary primary, IDictionary secondary) - { - if (secondary == null) - { - return primary; - } - if (primary == null) - { - return secondary; - } - var newWhere = new Dictionary(primary); - foreach (var pair in secondary) - { - var condition = pair.Value as IDictionary; - if (newWhere.ContainsKey(pair.Key)) - { - var oldCondition = newWhere[pair.Key] as IDictionary; - if (oldCondition == null || condition == null) - { - throw new ArgumentException("More than one where clause for the given key provided."); - } - var newCondition = new Dictionary(oldCondition); - foreach (var conditionPair in condition) - { - if (newCondition.ContainsKey(conditionPair.Key)) - { - throw new ArgumentException("More than one condition for the given key provided."); - } - newCondition[conditionPair.Key] = conditionPair.Value; - } - newWhere[pair.Key] = newCondition; - } - else - { - newWhere[pair.Key] = pair.Value; - } - } - return newWhere; - } - - /// - /// Constructs a query that is the or of the given queries. - /// - /// The list of AVQueries to 'or' together. - /// A AVeQquery that is the 'or' of the passed in queries. - public static Q Or(IEnumerable queries) - where Q : AVQueryBase - where O : IAVObject - { - string className = null; - var orValue = new List>(); - // We need to cast it to non-generic IEnumerable because of AOT-limitation - var nonGenericQueries = (IEnumerable)queries; - Q current = null; - foreach (var obj in nonGenericQueries) - { - var q = (Q)obj; - current = q; - if (className != null && q.className != className) - { - throw new ArgumentException( - "All of the queries in an or query must be on the same class."); - } - className = q.className; - var parameters = q.BuildParameters(); - if (parameters.Count == 0) - { - continue; - } - object where; - if (!parameters.TryGetValue("where", out where) || parameters.Count > 1) - { - throw new ArgumentException( - "None of the queries in an or query can have non-filtering clauses"); - } - orValue.Add(where as IDictionary); - } - current.Where = new Dictionary() - { - {"$or", orValue} - }; - return current; - } - - #region Order By - - /// - /// Sorts the results in ascending order by the given key. - /// This will override any existing ordering for the query. - /// - /// The key to order by. - /// A new query with the additional constraint. - public virtual S OrderBy(string key) - { - return CreateInstance(replacementOrderBy: new List { key }); - } - - /// - /// Sorts the results in descending order by the given key. - /// This will override any existing ordering for the query. - /// - /// The key to order by. - /// A new query with the additional constraint. - public virtual S OrderByDescending(string key) - { - return CreateInstance(replacementOrderBy: new List { "-" + key }); - } - - /// - /// Sorts the results in ascending order by the given key, after previous - /// ordering has been applied. - /// - /// This method can only be called if there is already an - /// or - /// on this query. - /// - /// The key to order by. - /// A new query with the additional constraint. - public virtual S ThenBy(string key) - { - return CreateInstance(thenBy: new List { key }); - } - - /// - /// Sorts the results in descending order by the given key, after previous - /// ordering has been applied. - /// - /// This method can only be called if there is already an - /// or on this query. - /// - /// The key to order by. - /// A new query with the additional constraint. - public virtual S ThenByDescending(string key) - { - return CreateInstance(thenBy: new List { "-" + key }); - } - - #endregion - - /// - /// Include nested AVObjects for the provided key. You can use dot notation - /// to specify which fields in the included objects should also be fetched. - /// - /// The key that should be included. - /// A new query with the additional constraint. - public virtual S Include(string key) - { - return CreateInstance(includes: new List { key }); - } - - /// - /// Restrict the fields of returned AVObjects to only include the provided key. - /// If this is called multiple times, then all of the keys specified in each of - /// the calls will be included. - /// - /// The key that should be included. - /// A new query with the additional constraint. - public virtual S Select(string key) - { - return CreateInstance(selectedKeys: new List { key }); - } - - /// - /// Skips a number of results before returning. This is useful for pagination - /// of large queries. Chaining multiple skips together will cause more results - /// to be skipped. - /// - /// The number of results to skip. - /// A new query with the additional constraint. - public virtual S Skip(int count) - { - return CreateInstance(skip: count); - } - - /// - /// Controls the maximum number of results that are returned. Setting a negative - /// limit denotes retrieval without a limit. Chaining multiple limits - /// results in the last limit specified being used. The default limit is - /// 100, with a maximum of 1000 results being returned at a time. - /// - /// The maximum number of results to return. - /// A new query with the additional constraint. - public virtual S Limit(int count) - { - return CreateInstance(limit: count); - } - - internal virtual S RedirectClassName(String key) - { - return CreateInstance(redirectClassNameForKey: key); - } - - #region Where - - /// - /// Adds a constraint to the query that requires a particular key's value to be - /// contained in the provided list of values. - /// - /// The key to check. - /// The values that will match. - /// A new query with the additional constraint. - public virtual S WhereContainedIn(string key, IEnumerable values) - { - return CreateInstance(where: new Dictionary { - { key, new Dictionary{{"$in", values.ToList()}}} - }); - } - - /// - /// Add a constraint to the querey that requires a particular key's value to be - /// a list containing all of the elements in the provided list of values. - /// - /// The key to check. - /// The values that will match. - /// A new query with the additional constraint. - public virtual S WhereContainsAll(string key, IEnumerable values) - { - return CreateInstance(where: new Dictionary { - { key, new Dictionary{{"$all", values.ToList()}}} - }); - } - - /// - /// Adds a constraint for finding string values that contain a provided string. - /// This will be slow for large data sets. - /// - /// The key that the string to match is stored in. - /// The substring that the value must contain. - /// A new query with the additional constraint. - public virtual S WhereContains(string key, string substring) - { - return CreateInstance(where: new Dictionary { - { key, new Dictionary{{"$regex", RegexQuote(substring)}}} - }); - } - - /// - /// Adds a constraint for finding objects that do not contain a given key. - /// - /// The key that should not exist. - /// A new query with the additional constraint. - public virtual S WhereDoesNotExist(string key) - { - return CreateInstance(where: new Dictionary{ - { key, new Dictionary{{"$exists", false}}} - }); - } - - /// - /// Adds a constraint to the query that requires that a particular key's value - /// does not match another AVQuery. This only works on keys whose values are - /// AVObjects or lists of AVObjects. - /// - /// The key to check. - /// The query that the value should not match. - /// A new query with the additional constraint. - public virtual S WhereDoesNotMatchQuery(string key, AVQuery query) - where TOther : AVObject - { - return CreateInstance(where: new Dictionary { - { key, new Dictionary{{"$notInQuery", query.BuildParameters(true)}}} - }); - } - - /// - /// Adds a constraint for finding string values that end with a provided string. - /// This will be slow for large data sets. - /// - /// The key that the string to match is stored in. - /// The substring that the value must end with. - /// A new query with the additional constraint. - public virtual S WhereEndsWith(string key, string suffix) - { - return CreateInstance(where: new Dictionary { - { key, new Dictionary{{"$regex", RegexQuote(suffix) + "$"}}} - }); - } - - /// - /// Adds a constraint to the query that requires a particular key's value to be - /// equal to the provided value. - /// - /// The key to check. - /// The value that the AVObject must contain. - /// A new query with the additional constraint. - public virtual S WhereEqualTo(string key, object value) - { - return CreateInstance(where: new Dictionary { - { key, value} - }); - } - - /// - /// Adds a constraint to the query that requires a particular key's size to be - /// equal to the provided size. - /// - /// The size equal to. - /// The key to check. - /// The value that the size must be. - /// A new query with the additional constraint. - public virtual S WhereSizeEqualTo(string key, uint size) - { - return CreateInstance(where: new Dictionary { - { key, new Dictionary{{"$size", size}}} - }); - } - - /// - /// Adds a constraint for finding objects that contain a given key. - /// - /// The key that should exist. - /// A new query with the additional constraint. - public virtual S WhereExists(string key) - { - return CreateInstance(where: new Dictionary{ - { key, new Dictionary{{"$exists", true}}} - }); - } - - /// - /// Adds a constraint to the query that requires a particular key's value to be - /// greater than the provided value. - /// - /// The key to check. - /// The value that provides a lower bound. - /// A new query with the additional constraint. - public virtual S WhereGreaterThan(string key, object value) - { - return CreateInstance(where: new Dictionary{ - { key, new Dictionary{{"$gt", value}}} - }); - } - - /// - /// Adds a constraint to the query that requires a particular key's value to be - /// greater or equal to than the provided value. - /// - /// The key to check. - /// The value that provides a lower bound. - /// A new query with the additional constraint. - public virtual S WhereGreaterThanOrEqualTo(string key, object value) - { - return CreateInstance(where: new Dictionary{ - { key, new Dictionary{{"$gte", value}}} - }); - } - - /// - /// Adds a constraint to the query that requires a particular key's value to be - /// less than the provided value. - /// - /// The key to check. - /// The value that provides an upper bound. - /// A new query with the additional constraint. - public virtual S WhereLessThan(string key, object value) - { - return CreateInstance(where: new Dictionary{ - { key, new Dictionary{{"$lt", value}}} - }); - } - - /// - /// Adds a constraint to the query that requires a particular key's value to be - /// less than or equal to the provided value. - /// - /// The key to check. - /// The value that provides a lower bound. - /// A new query with the additional constraint. - public virtual S WhereLessThanOrEqualTo(string key, object value) - { - return CreateInstance(where: new Dictionary{ - { key, new Dictionary{{"$lte", value}}} - }); - } - - /// - /// Adds a regular expression constraint for finding string values that match the provided - /// regular expression. This may be slow for large data sets. - /// - /// The key that the string to match is stored in. - /// The regular expression pattern to match. The Regex must - /// have the options flag set. - /// Any of the following supported PCRE modifiers: - /// i - Case insensitive search - /// m Search across multiple lines of input - /// A new query with the additional constraint. - public virtual S WhereMatches(string key, Regex regex, string modifiers) - { - if (!regex.Options.HasFlag(RegexOptions.ECMAScript)) - { - throw new ArgumentException( - "Only ECMAScript-compatible regexes are supported. Please use the ECMAScript RegexOptions flag when creating your regex."); - } - return CreateInstance(where: new Dictionary { - { key, EncodeRegex(regex, modifiers)} - }); - } - - /// - /// Adds a regular expression constraint for finding string values that match the provided - /// regular expression. This may be slow for large data sets. - /// - /// The key that the string to match is stored in. - /// The regular expression pattern to match. The Regex must - /// have the options flag set. - /// A new query with the additional constraint. - public virtual S WhereMatches(string key, Regex regex) - { - return WhereMatches(key, regex, null); - } - - /// - /// Adds a regular expression constraint for finding string values that match the provided - /// regular expression. This may be slow for large data sets. - /// - /// The key that the string to match is stored in. - /// The PCRE regular expression pattern to match. - /// Any of the following supported PCRE modifiers: - /// i - Case insensitive search - /// m Search across multiple lines of input - /// A new query with the additional constraint. - public virtual S WhereMatches(string key, string pattern, string modifiers = null) - { - return WhereMatches(key, new Regex(pattern, RegexOptions.ECMAScript), modifiers); - } - - /// - /// Adds a regular expression constraint for finding string values that match the provided - /// regular expression. This may be slow for large data sets. - /// - /// The key that the string to match is stored in. - /// The PCRE regular expression pattern to match. - /// A new query with the additional constraint. - public virtual S WhereMatches(string key, string pattern) - { - return WhereMatches(key, pattern, null); - } - - /// - /// Adds a constraint to the query that requires a particular key's value - /// to match a value for a key in the results of another AVQuery. - /// - /// The key whose value is being checked. - /// The key in the objects from the subquery to look in. - /// The subquery to run - /// A new query with the additional constraint. - public virtual S WhereMatchesKeyInQuery(string key, - string keyInQuery, - AVQuery query) where TOther : AVObject - { - var parameters = new Dictionary { - { "query", query.BuildParameters(true)}, - { "key", keyInQuery} - }; - return CreateInstance(where: new Dictionary { - { key, new Dictionary{{"$select", parameters}}} - }); - } - - /// - /// Adds a constraint to the query that requires a particular key's value - /// does not match any value for a key in the results of another AVQuery. - /// - /// The key whose value is being checked. - /// The key in the objects from the subquery to look in. - /// The subquery to run - /// A new query with the additional constraint. - public virtual S WhereDoesNotMatchesKeyInQuery(string key, - string keyInQuery, - AVQuery query) where TOther : AVObject - { - var parameters = new Dictionary { - { "query", query.BuildParameters(true)}, - { "key", keyInQuery} - }; - return CreateInstance(where: new Dictionary { - { key, new Dictionary{{"$dontSelect", parameters}}} - }); - } - - /// - /// Adds a constraint to the query that requires that a particular key's value - /// matches another AVQuery. This only works on keys whose values are - /// AVObjects or lists of AVObjects. - /// - /// The key to check. - /// The query that the value should match. - /// A new query with the additional constraint. - public virtual S WhereMatchesQuery(string key, AVQuery query) - where TOther : AVObject - { - return CreateInstance(where: new Dictionary { - { key, new Dictionary{{"$inQuery", query.BuildParameters(true)}}} - }); - } - - /// - /// Adds a proximity-based constraint for finding objects with keys whose GeoPoint - /// values are near the given point. - /// - /// The key that the AVGeoPoint is stored in. - /// The reference AVGeoPoint. - /// A new query with the additional constraint. - public virtual S WhereNear(string key, AVGeoPoint point) - { - return CreateInstance(where: new Dictionary { - { key, new Dictionary{{"$nearSphere", point}}} - }); - } - - /// - /// Adds a constraint to the query that requires a particular key's value to be - /// contained in the provided list of values. - /// - /// The key to check. - /// The values that will match. - /// A new query with the additional constraint. - public virtual S WhereNotContainedIn(string key, IEnumerable values) - { - return CreateInstance(where: new Dictionary { - { key, new Dictionary{{"$nin", values.ToList()}}} - }); - } - - /// - /// Adds a constraint to the query that requires a particular key's value not - /// to be equal to the provided value. - /// - /// The key to check. - /// The value that that must not be equalled. - /// A new query with the additional constraint. - public virtual S WhereNotEqualTo(string key, object value) - { - return CreateInstance(where: new Dictionary { - { key, new Dictionary{{"$ne", value}}} - }); - } - - /// - /// Adds a constraint for finding string values that start with the provided string. - /// This query will use the backend index, so it will be fast even with large data sets. - /// - /// The key that the string to match is stored in. - /// The substring that the value must start with. - /// A new query with the additional constraint. - public virtual S WhereStartsWith(string key, string suffix) - { - return CreateInstance(where: new Dictionary { - { key, new Dictionary{{"$regex", "^" + RegexQuote(suffix)}}} - }); - } - - /// - /// Add a constraint to the query that requires a particular key's coordinates to be - /// contained within a given rectangular geographic bounding box. - /// - /// The key to be constrained. - /// The lower-left inclusive corner of the box. - /// The upper-right inclusive corner of the box. - /// A new query with the additional constraint. - public virtual S WhereWithinGeoBox(string key, - AVGeoPoint southwest, - AVGeoPoint northeast) - { - - return this.CreateInstance(where: new Dictionary - { - { - key, - new Dictionary - { - { - "$within", - new Dictionary { - { "$box", new[] {southwest, northeast}} - } - } - } - } - }); - } - - /// - /// Adds a proximity-based constraint for finding objects with keys whose GeoPoint - /// values are near the given point and within the maximum distance given. - /// - /// The key that the AVGeoPoint is stored in. - /// The reference AVGeoPoint. - /// The maximum distance (in radians) of results to return. - /// A new query with the additional constraint. - public virtual S WhereWithinDistance( - string key, AVGeoPoint point, AVGeoDistance maxDistance) - { - var nearWhere = new Dictionary { - { key, new Dictionary{{"$nearSphere", point}}} - }; - var mergedWhere = MergeWhere(nearWhere, new Dictionary { - { key, new Dictionary{{"$maxDistance", maxDistance.Radians}}} - }); - return CreateInstance(where: mergedWhere); - } - - internal virtual S WhereRelatedTo(AVObject parent, string key) - { - return CreateInstance(where: new Dictionary { - { - "$relatedTo", - new Dictionary { - { "object", parent}, - { "key", key} - } - } - }); - } - - #endregion - - /// - /// Retrieves a list of AVObjects that satisfy this query from LeanCloud. - /// - /// The list of AVObjects that match this query. - public virtual Task> FindAsync() - { - return FindAsync(CancellationToken.None); - } - - /// - /// Retrieves a list of AVObjects that satisfy this query from LeanCloud. - /// - /// The cancellation token. - /// The list of AVObjects that match this query. - public abstract Task> FindAsync(CancellationToken cancellationToken); - - - /// - /// Retrieves at most one AVObject that satisfies this query. - /// - /// A single AVObject that satisfies this query, or else null. - public virtual Task FirstOrDefaultAsync() - { - return FirstOrDefaultAsync(CancellationToken.None); - } - - /// - /// Retrieves at most one AVObject that satisfies this query. - /// - /// The cancellation token. - /// A single AVObject that satisfies this query, or else null. - public abstract Task FirstOrDefaultAsync(CancellationToken cancellationToken); - - /// - /// Retrieves at most one AVObject that satisfies this query. - /// - /// A single AVObject that satisfies this query. - /// If no results match the query. - public virtual Task FirstAsync() - { - return FirstAsync(CancellationToken.None); - } - - /// - /// Retrieves at most one AVObject that satisfies this query. - /// - /// The cancellation token. - /// A single AVObject that satisfies this query. - /// If no results match the query. - public abstract Task FirstAsync(CancellationToken cancellationToken); - - /// - /// Counts the number of objects that match this query. - /// - /// The number of objects that match this query. - public virtual Task CountAsync() - { - return CountAsync(CancellationToken.None); - } - - /// - /// Counts the number of objects that match this query. - /// - /// The cancellation token. - /// The number of objects that match this query. - public abstract Task CountAsync(CancellationToken cancellationToken); - - /// - /// Constructs a AVObject whose id is already known by fetching data - /// from the server. - /// - /// ObjectId of the AVObject to fetch. - /// The AVObject for the given objectId. - public virtual Task GetAsync(string objectId) - { - return GetAsync(objectId, CancellationToken.None); - } - - /// - /// Constructs a AVObject whose id is already known by fetching data - /// from the server. - /// - /// ObjectId of the AVObject to fetch. - /// The cancellation token. - /// The AVObject for the given objectId. - public abstract Task GetAsync(string objectId, CancellationToken cancellationToken); - - internal object GetConstraint(string key) - { - return where == null ? null : where.GetOrDefault(key, null); - } - - /// - /// 构建查询字符串 - /// - /// 是否包含 ClassName - /// - public IDictionary BuildParameters(bool includeClassName = false) - { - Dictionary result = new Dictionary(); - if (where != null) - { - result["where"] = PointerOrLocalIdEncoder.Instance.Encode(where); - } - if (orderBy != null) - { - result["order"] = string.Join(",", orderBy.ToArray()); - } - if (skip != null) - { - result["skip"] = skip.Value; - } - if (limit != null) - { - result["limit"] = limit.Value; - } - if (includes != null) - { - result["include"] = string.Join(",", includes.ToArray()); - } - if (selectedKeys != null) - { - result["keys"] = string.Join(",", selectedKeys.ToArray()); - } - if (includeClassName) - { - result["className"] = className; - } - if (redirectClassNameForKey != null) - { - result["redirectClassNameForKey"] = redirectClassNameForKey; - } - return result; - } - - private string RegexQuote(string input) - { - return "\\Q" + input.Replace("\\E", "\\E\\\\E\\Q") + "\\E"; - } - - private string GetRegexOptions(Regex regex, string modifiers) - { - string result = modifiers ?? ""; - if (regex.Options.HasFlag(RegexOptions.IgnoreCase) && !modifiers.Contains("i")) - { - result += "i"; - } - if (regex.Options.HasFlag(RegexOptions.Multiline) && !modifiers.Contains("m")) - { - result += "m"; - } - return result; - } - - private IDictionary EncodeRegex(Regex regex, string modifiers) - { - var options = GetRegexOptions(regex, modifiers); - var dict = new Dictionary(); - dict["$regex"] = regex.ToString(); - if (!string.IsNullOrEmpty(options)) - { - dict["$options"] = options; - } - return dict; - } - } - - //public abstract class AVQueryBase : IAVQueryTuple - // where T : IAVObject - //{ - // protected readonly string className; - // protected readonly Dictionary where; - // protected readonly ReadOnlyCollection orderBy; - // protected readonly ReadOnlyCollection includes; - // protected readonly ReadOnlyCollection selectedKeys; - // protected readonly String redirectClassNameForKey; - // protected readonly int? skip; - // protected readonly int? limit; - - // internal string ClassName { get { return className; } } - - // private string relativeUri; - // internal string RelativeUri - // { - // get - // { - // string rtn = string.Empty; - // if (string.IsNullOrEmpty(relativeUri)) - // { - // rtn = "classes/" + Uri.EscapeDataString(this.className); - // } - // else - // { - // rtn = relativeUri; - // } - // return rtn; - // } - // set - // { - // relativeUri = value; - // } - // } - // public Dictionary Condition - // { - // get { return this.where; } - // } - - // protected AVQueryBase() - // { - - // } - - // internal abstract S CreateInstance(AVQueryBase source, - // IDictionary where = null, - // IEnumerable replacementOrderBy = null, - // IEnumerable thenBy = null, - // int? skip = null, - // int? limit = null, - // IEnumerable includes = null, - // IEnumerable selectedKeys = null, - // String redirectClassNameForKey = null); - - // /// - // /// Private constructor for composition of queries. A Source query is required, - // /// but the remaining values can be null if they won't be changed in this - // /// composition. - // /// - // protected AVQueryBase(AVQueryBase source, - // IDictionary where = null, - // IEnumerable replacementOrderBy = null, - // IEnumerable thenBy = null, - // int? skip = null, - // int? limit = null, - // IEnumerable includes = null, - // IEnumerable selectedKeys = null, - // String redirectClassNameForKey = null) - // { - // if (source == null) - // { - // throw new ArgumentNullException("Source"); - // } - - // className = source.className; - // this.where = source.where; - // this.orderBy = source.orderBy; - // this.skip = source.skip; - // this.limit = source.limit; - // this.includes = source.includes; - // this.selectedKeys = source.selectedKeys; - // this.redirectClassNameForKey = source.redirectClassNameForKey; - - // if (where != null) - // { - // var newWhere = MergeWhereClauses(where); - // this.where = new Dictionary(newWhere); - // } - - // if (replacementOrderBy != null) - // { - // this.orderBy = new ReadOnlyCollection(replacementOrderBy.ToList()); - // } - - // if (thenBy != null) - // { - // if (this.orderBy == null) - // { - // throw new ArgumentException("You must call OrderBy before calling ThenBy."); - // } - // var newOrderBy = new List(this.orderBy); - // newOrderBy.AddRange(thenBy); - // this.orderBy = new ReadOnlyCollection(newOrderBy); - // } - - // // Remove duplicates. - // if (this.orderBy != null) - // { - // var newOrderBy = new HashSet(this.orderBy); - // this.orderBy = new ReadOnlyCollection(newOrderBy.ToList()); - // } - - // if (skip != null) - // { - // this.skip = (this.skip ?? 0) + skip; - // } - - // if (limit != null) - // { - // this.limit = limit; - // } - - // if (includes != null) - // { - // var newIncludes = MergeIncludes(includes); - // this.includes = new ReadOnlyCollection(newIncludes.ToList()); - // } - - // if (selectedKeys != null) - // { - // var newSelectedKeys = MergeSelectedKeys(selectedKeys); - // this.selectedKeys = new ReadOnlyCollection(newSelectedKeys.ToList()); - // } - - // if (redirectClassNameForKey != null) - // { - // this.redirectClassNameForKey = redirectClassNameForKey; - // } - // } - - // public AVQueryBase(string className) - // { - // if (string.IsNullOrEmpty(className)) - // { - // throw new ArgumentNullException("className", "Must specify a AVObject class name when creating a AVQuery."); - // } - // this.className = className; - // } - - // private HashSet MergeIncludes(IEnumerable includes) - // { - // if (this.includes == null) - // { - // return new HashSet(includes); - // } - // var newIncludes = new HashSet(this.includes); - // foreach (var item in includes) - // { - // newIncludes.Add(item); - // } - // return newIncludes; - // } - - // private HashSet MergeSelectedKeys(IEnumerable selectedKeys) - // { - // if (this.selectedKeys == null) - // { - // return new HashSet(selectedKeys); - // } - // var newSelectedKeys = new HashSet(this.selectedKeys); - // foreach (var item in selectedKeys) - // { - // newSelectedKeys.Add(item); - // } - // return newSelectedKeys; - // } - - // private IDictionary MergeWhereClauses(IDictionary where) - // { - // return MergeWhere(this.where, where); - // } - - // public virtual IDictionary MergeWhere(IDictionary primary, IDictionary secondary) - // { - // if (secondary == null) - // { - // return primary; - // } - // var newWhere = new Dictionary(primary); - // foreach (var pair in secondary) - // { - // var condition = pair.Value as IDictionary; - // if (newWhere.ContainsKey(pair.Key)) - // { - // var oldCondition = newWhere[pair.Key] as IDictionary; - // if (oldCondition == null || condition == null) - // { - // throw new ArgumentException("More than one where clause for the given key provided."); - // } - // var newCondition = new Dictionary(oldCondition); - // foreach (var conditionPair in condition) - // { - // if (newCondition.ContainsKey(conditionPair.Key)) - // { - // throw new ArgumentException("More than one condition for the given key provided."); - // } - // newCondition[conditionPair.Key] = conditionPair.Value; - // } - // newWhere[pair.Key] = newCondition; - // } - // else - // { - // newWhere[pair.Key] = pair.Value; - // } - // } - // return newWhere; - // } - - // ///// - // ///// Constructs a query that is the or of the given queries. - // ///// - // ///// The list of AVQueries to 'or' together. - // ///// A AVQquery that is the 'or' of the passed in queries. - // //public static AVQuery Or(IEnumerable> queries) - // //{ - // // string className = null; - // // var orValue = new List>(); - // // // We need to cast it to non-generic IEnumerable because of AOT-limitation - // // var nonGenericQueries = (IEnumerable)queries; - // // foreach (var obj in nonGenericQueries) - // // { - // // var q = (AVQuery)obj; - // // if (className != null && q.className != className) - // // { - // // throw new ArgumentException( - // // "All of the queries in an or query must be on the same class."); - // // } - // // className = q.className; - // // var parameters = q.BuildParameters(); - // // if (parameters.Count == 0) - // // { - // // continue; - // // } - // // object where; - // // if (!parameters.TryGetValue("where", out where) || parameters.Count > 1) - // // { - // // throw new ArgumentException( - // // "None of the queries in an or query can have non-filtering clauses"); - // // } - // // orValue.Add(where as IDictionary); - // // } - // // return new AVQuery(new AVQuery(className), - // // where: new Dictionary { - // // {"$or", orValue} - // // }); - // //} - - // #region Order By - - // /// - // /// Sorts the results in ascending order by the given key. - // /// This will override any existing ordering for the query. - // /// - // /// The key to order by. - // /// A new query with the additional constraint. - // public virtual S OrderBy(string key) - // { - // return CreateInstance( replacementOrderBy: new List { key }); - // } - - // /// - // /// Sorts the results in descending order by the given key. - // /// This will override any existing ordering for the query. - // /// - // /// The key to order by. - // /// A new query with the additional constraint. - // public virtual S OrderByDescending(string key) - // { - // return CreateInstance( replacementOrderBy: new List { "-" + key }); - // } - - // /// - // /// Sorts the results in ascending order by the given key, after previous - // /// ordering has been applied. - // /// - // /// This method can only be called if there is already an - // /// or - // /// on this query. - // /// - // /// The key to order by. - // /// A new query with the additional constraint. - // public virtual S ThenBy(string key) - // { - // return CreateInstance( thenBy: new List { key }); - // } - - // /// - // /// Sorts the results in descending order by the given key, after previous - // /// ordering has been applied. - // /// - // /// This method can only be called if there is already an - // /// or on this query. - // /// - // /// The key to order by. - // /// A new query with the additional constraint. - // public virtual S ThenByDescending(string key) - // { - // return CreateInstance( thenBy: new List { "-" + key }); - // } - - // #endregion - - // /// - // /// Include nested AVObjects for the provided key. You can use dot notation - // /// to specify which fields in the included objects should also be fetched. - // /// - // /// The key that should be included. - // /// A new query with the additional constraint. - // public virtual S Include(string key) - // { - // return CreateInstance( includes: new List { key }); - // } - - // /// - // /// Restrict the fields of returned AVObjects to only include the provided key. - // /// If this is called multiple times, then all of the keys specified in each of - // /// the calls will be included. - // /// - // /// The key that should be included. - // /// A new query with the additional constraint. - // public virtual S Select(string key) - // { - // return CreateInstance( selectedKeys: new List { key }); - // } - - // /// - // /// Skips a number of results before returning. This is useful for pagination - // /// of large queries. Chaining multiple skips together will cause more results - // /// to be skipped. - // /// - // /// The number of results to skip. - // /// A new query with the additional constraint. - // public virtual S Skip(int count) - // { - // return CreateInstance( skip: count); - // } - - // /// - // /// Controls the maximum number of results that are returned. Setting a negative - // /// limit denotes retrieval without a limit. Chaining multiple limits - // /// results in the last limit specified being used. The default limit is - // /// 100, with a maximum of 1000 results being returned at a time. - // /// - // /// The maximum number of results to return. - // /// A new query with the additional constraint. - // public virtual S Limit(int count) - // { - // return CreateInstance( limit: count); - // } - - // internal virtual S RedirectClassName(String key) - // { - // return CreateInstance( redirectClassNameForKey: key); - // } - - // #region Where - - // /// - // /// Adds a constraint to the query that requires a particular key's value to be - // /// contained in the provided list of values. - // /// - // /// The key to check. - // /// The values that will match. - // /// A new query with the additional constraint. - // public virtual S WhereContainedIn(string key, IEnumerable values) - // { - // return CreateInstance( where: new Dictionary { - // { key, new Dictionary{{"$in", values.ToList()}}} - // }); - // } - - // /// - // /// Add a constraint to the querey that requires a particular key's value to be - // /// a list containing all of the elements in the provided list of values. - // /// - // /// The key to check. - // /// The values that will match. - // /// A new query with the additional constraint. - // public virtual S WhereContainsAll(string key, IEnumerable values) - // { - // return CreateInstance( where: new Dictionary { - // { key, new Dictionary{{"$all", values.ToList()}}} - // }); - // } - - // /// - // /// Adds a constraint for finding string values that contain a provided string. - // /// This will be slow for large data sets. - // /// - // /// The key that the string to match is stored in. - // /// The substring that the value must contain. - // /// A new query with the additional constraint. - // public virtual S WhereContains(string key, string substring) - // { - // return CreateInstance( where: new Dictionary { - // { key, new Dictionary{{"$regex", RegexQuote(substring)}}} - // }); - // } - - // /// - // /// Adds a constraint for finding objects that do not contain a given key. - // /// - // /// The key that should not exist. - // /// A new query with the additional constraint. - // public virtual S WhereDoesNotExist(string key) - // { - // return CreateInstance( where: new Dictionary{ - // { key, new Dictionary{{"$exists", false}}} - // }); - // } - - // /// - // /// Adds a constraint to the query that requires that a particular key's value - // /// does not match another AVQuery. This only works on keys whose values are - // /// AVObjects or lists of AVObjects. - // /// - // /// The key to check. - // /// The query that the value should not match. - // /// A new query with the additional constraint. - // public virtual S WhereDoesNotMatchQuery(string key, AVQuery query) - // where TOther : AVObject - // { - // return CreateInstance( where: new Dictionary { - // { key, new Dictionary{{"$notInQuery", query.BuildParameters(true)}}} - // }); - // } - - // /// - // /// Adds a constraint for finding string values that end with a provided string. - // /// This will be slow for large data sets. - // /// - // /// The key that the string to match is stored in. - // /// The substring that the value must end with. - // /// A new query with the additional constraint. - // public virtual S WhereEndsWith(string key, string suffix) - // { - // return CreateInstance( where: new Dictionary { - // { key, new Dictionary{{"$regex", RegexQuote(suffix) + "$"}}} - // }); - // } - - // /// - // /// Adds a constraint to the query that requires a particular key's value to be - // /// equal to the provided value. - // /// - // /// The key to check. - // /// The value that the AVObject must contain. - // /// A new query with the additional constraint. - // public virtual S WhereEqualTo(string key, object value) - // { - // return CreateInstance( where: new Dictionary { - // { key, value} - // }); - // } - - // /// - // /// Adds a constraint to the query that requires a particular key's size to be - // /// equal to the provided size. - // /// - // /// The size equal to. - // /// The key to check. - // /// The value that the size must be. - // /// A new query with the additional constraint. - // public virtual S WhereSizeEqualTo(string key, uint size) - // { - // return CreateInstance( where: new Dictionary { - // { key, new Dictionary{{"$size", size}}} - // }); - // } - - // /// - // /// Adds a constraint for finding objects that contain a given key. - // /// - // /// The key that should exist. - // /// A new query with the additional constraint. - // public virtual S WhereExists(string key) - // { - // return CreateInstance( where: new Dictionary{ - // { key, new Dictionary{{"$exists", true}}} - // }); - // } - - // /// - // /// Adds a constraint to the query that requires a particular key's value to be - // /// greater than the provided value. - // /// - // /// The key to check. - // /// The value that provides a lower bound. - // /// A new query with the additional constraint. - // public virtual S WhereGreaterThan(string key, object value) - // { - // return CreateInstance( where: new Dictionary{ - // { key, new Dictionary{{"$gt", value}}} - // }); - // } - - // /// - // /// Adds a constraint to the query that requires a particular key's value to be - // /// greater or equal to than the provided value. - // /// - // /// The key to check. - // /// The value that provides a lower bound. - // /// A new query with the additional constraint. - // public virtual S WhereGreaterThanOrEqualTo(string key, object value) - // { - // return CreateInstance( where: new Dictionary{ - // { key, new Dictionary{{"$gte", value}}} - // }); - // } - - // /// - // /// Adds a constraint to the query that requires a particular key's value to be - // /// less than the provided value. - // /// - // /// The key to check. - // /// The value that provides an upper bound. - // /// A new query with the additional constraint. - // public virtual S WhereLessThan(string key, object value) - // { - // return CreateInstance( where: new Dictionary{ - // { key, new Dictionary{{"$lt", value}}} - // }); - // } - - // /// - // /// Adds a constraint to the query that requires a particular key's value to be - // /// less than or equal to the provided value. - // /// - // /// The key to check. - // /// The value that provides a lower bound. - // /// A new query with the additional constraint. - // public virtual S WhereLessThanOrEqualTo(string key, object value) - // { - // return CreateInstance( where: new Dictionary{ - // { key, new Dictionary{{"$lte", value}}} - // }); - // } - - // /// - // /// Adds a regular expression constraint for finding string values that match the provided - // /// regular expression. This may be slow for large data sets. - // /// - // /// The key that the string to match is stored in. - // /// The regular expression pattern to match. The Regex must - // /// have the options flag set. - // /// Any of the following supported PCRE modifiers: - // /// i - Case insensitive search - // /// m Search across multiple lines of input - // /// A new query with the additional constraint. - // public virtual S WhereMatches(string key, Regex regex, string modifiers) - // { - // if (!regex.Options.HasFlag(RegexOptions.ECMAScript)) - // { - // throw new ArgumentException( - // "Only ECMAScript-compatible regexes are supported. Please use the ECMAScript RegexOptions flag when creating your regex."); - // } - // return CreateInstance( where: new Dictionary { - // { key, EncodeRegex(regex, modifiers)} - // }); - // } - - // /// - // /// Adds a regular expression constraint for finding string values that match the provided - // /// regular expression. This may be slow for large data sets. - // /// - // /// The key that the string to match is stored in. - // /// The regular expression pattern to match. The Regex must - // /// have the options flag set. - // /// A new query with the additional constraint. - // public virtual S WhereMatches(string key, Regex regex) - // { - // return WhereMatches(key, regex, null); - // } - - // /// - // /// Adds a regular expression constraint for finding string values that match the provided - // /// regular expression. This may be slow for large data sets. - // /// - // /// The key that the string to match is stored in. - // /// The PCRE regular expression pattern to match. - // /// Any of the following supported PCRE modifiers: - // /// i - Case insensitive search - // /// m Search across multiple lines of input - // /// A new query with the additional constraint. - // public virtual S WhereMatches(string key, string pattern, string modifiers = null) - // { - // return WhereMatches(key, new Regex(pattern, RegexOptions.ECMAScript), modifiers); - // } - - // /// - // /// Adds a regular expression constraint for finding string values that match the provided - // /// regular expression. This may be slow for large data sets. - // /// - // /// The key that the string to match is stored in. - // /// The PCRE regular expression pattern to match. - // /// A new query with the additional constraint. - // public virtual S WhereMatches(string key, string pattern) - // { - // return WhereMatches(key, pattern, null); - // } - - // /// - // /// Adds a constraint to the query that requires a particular key's value - // /// to match a value for a key in the results of another AVQuery. - // /// - // /// The key whose value is being checked. - // /// The key in the objects from the subquery to look in. - // /// The subquery to run - // /// A new query with the additional constraint. - // public virtual S WhereMatchesKeyInQuery(string key, - // string keyInQuery, - // AVQuery query) where TOther : AVObject - // { - // var parameters = new Dictionary { - // { "query", query.BuildParameters(true)}, - // { "key", keyInQuery} - // }; - // return CreateInstance( where: new Dictionary { - // { key, new Dictionary{{"$select", parameters}}} - // }); - // } - - // /// - // /// Adds a constraint to the query that requires a particular key's value - // /// does not match any value for a key in the results of another AVQuery. - // /// - // /// The key whose value is being checked. - // /// The key in the objects from the subquery to look in. - // /// The subquery to run - // /// A new query with the additional constraint. - // public virtual S WhereDoesNotMatchesKeyInQuery(string key, - // string keyInQuery, - // AVQuery query) where TOther : AVObject - // { - // var parameters = new Dictionary { - // { "query", query.BuildParameters(true)}, - // { "key", keyInQuery} - // }; - // return CreateInstance( where: new Dictionary { - // { key, new Dictionary{{"$dontSelect", parameters}}} - // }); - // } - - // /// - // /// Adds a constraint to the query that requires that a particular key's value - // /// matches another AVQuery. This only works on keys whose values are - // /// AVObjects or lists of AVObjects. - // /// - // /// The key to check. - // /// The query that the value should match. - // /// A new query with the additional constraint. - // public virtual S WhereMatchesQuery(string key, AVQuery query) - // where TOther : AVObject - // { - // return CreateInstance( where: new Dictionary { - // { key, new Dictionary{{"$inQuery", query.BuildParameters(true)}}} - // }); - // } - - // /// - // /// Adds a proximity-based constraint for finding objects with keys whose GeoPoint - // /// values are near the given point. - // /// - // /// The key that the AVGeoPoint is stored in. - // /// The reference AVGeoPoint. - // /// A new query with the additional constraint. - // public virtual S WhereNear(string key, AVGeoPoint point) - // { - // return CreateInstance( where: new Dictionary { - // { key, new Dictionary{{"$nearSphere", point}}} - // }); - // } - - // /// - // /// Adds a constraint to the query that requires a particular key's value to be - // /// contained in the provided list of values. - // /// - // /// The key to check. - // /// The values that will match. - // /// A new query with the additional constraint. - // public virtual S WhereNotContainedIn(string key, IEnumerable values) - // { - // return CreateInstance( where: new Dictionary { - // { key, new Dictionary{{"$nin", values.ToList()}}} - // }); - // } - - // /// - // /// Adds a constraint to the query that requires a particular key's value not - // /// to be equal to the provided value. - // /// - // /// The key to check. - // /// The value that that must not be equalled. - // /// A new query with the additional constraint. - // public virtual S WhereNotEqualTo(string key, object value) - // { - // return CreateInstance( where: new Dictionary { - // { key, new Dictionary{{"$ne", value}}} - // }); - // } - - // /// - // /// Adds a constraint for finding string values that start with the provided string. - // /// This query will use the backend index, so it will be fast even with large data sets. - // /// - // /// The key that the string to match is stored in. - // /// The substring that the value must start with. - // /// A new query with the additional constraint. - // public virtual S WhereStartsWith(string key, string suffix) - // { - // return CreateInstance( where: new Dictionary { - // { key, new Dictionary{{"$regex", "^" + RegexQuote(suffix)}}} - // }); - // } - - // /// - // /// Add a constraint to the query that requires a particular key's coordinates to be - // /// contained within a given rectangular geographic bounding box. - // /// - // /// The key to be constrained. - // /// The lower-left inclusive corner of the box. - // /// The upper-right inclusive corner of the box. - // /// A new query with the additional constraint. - // public virtual S WhereWithinGeoBox(string key, - // AVGeoPoint southwest, - // AVGeoPoint northeast) - // { - - // return this.CreateInstance( where: new Dictionary - // { - // { - // key, - // new Dictionary - // { - // { - // "$within", - // new Dictionary { - // { "$box", new[] {southwest, northeast}} - // } - // } - // } - // } - // }); - // } - - // /// - // /// Adds a proximity-based constraint for finding objects with keys whose GeoPoint - // /// values are near the given point and within the maximum distance given. - // /// - // /// The key that the AVGeoPoint is stored in. - // /// The reference AVGeoPoint. - // /// The maximum distance (in radians) of results to return. - // /// A new query with the additional constraint. - // public virtual S WhereWithinDistance( - // string key, AVGeoPoint point, AVGeoDistance maxDistance) - // { - // var nearWhere = new Dictionary { - // { key, new Dictionary{{"$nearSphere", point}}} - // }; - // var mergedWhere = MergeWhere(nearWhere, new Dictionary { - // { key, new Dictionary{{"$maxDistance", maxDistance.Radians}}} - // }); - // return CreateInstance( where: mergedWhere); - // } - - // internal virtual S WhereRelatedTo(AVObject parent, string key) - // { - // return CreateInstance( where: new Dictionary { - // { - // "$relatedTo", - // new Dictionary { - // { "object", parent}, - // { "key", key} - // } - // } - // }); - // } - - // #endregion - - // /// - // /// Retrieves a list of AVObjects that satisfy this query from LeanCloud. - // /// - // /// The list of AVObjects that match this query. - // public virtual Task> FindAsync() - // { - // return FindAsync(CancellationToken.None); - // } - - // /// - // /// Retrieves a list of AVObjects that satisfy this query from LeanCloud. - // /// - // /// The cancellation token. - // /// The list of AVObjects that match this query. - // public abstract Task> FindAsync(CancellationToken cancellationToken); - - - // /// - // /// Retrieves at most one AVObject that satisfies this query. - // /// - // /// A single AVObject that satisfies this query, or else null. - // public virtual Task FirstOrDefaultAsync() - // { - // return FirstOrDefaultAsync(CancellationToken.None); - // } - - // /// - // /// Retrieves at most one AVObject that satisfies this query. - // /// - // /// The cancellation token. - // /// A single AVObject that satisfies this query, or else null. - // public abstract Task FirstOrDefaultAsync(CancellationToken cancellationToken); - - // /// - // /// Retrieves at most one AVObject that satisfies this query. - // /// - // /// A single AVObject that satisfies this query. - // /// If no results match the query. - // public virtual Task FirstAsync() - // { - // return FirstAsync(CancellationToken.None); - // } - - // /// - // /// Retrieves at most one AVObject that satisfies this query. - // /// - // /// The cancellation token. - // /// A single AVObject that satisfies this query. - // /// If no results match the query. - // public abstract Task FirstAsync(CancellationToken cancellationToken); - - // /// - // /// Counts the number of objects that match this query. - // /// - // /// The number of objects that match this query. - // public virtual Task CountAsync() - // { - // return CountAsync(CancellationToken.None); - // } - - // /// - // /// Counts the number of objects that match this query. - // /// - // /// The cancellation token. - // /// The number of objects that match this query. - // public abstract Task CountAsync(CancellationToken cancellationToken); - - // /// - // /// Constructs a AVObject whose id is already known by fetching data - // /// from the server. - // /// - // /// ObjectId of the AVObject to fetch. - // /// The AVObject for the given objectId. - // public virtual Task GetAsync(string objectId) - // { - // return GetAsync(objectId, CancellationToken.None); - // } - - // /// - // /// Constructs a AVObject whose id is already known by fetching data - // /// from the server. - // /// - // /// ObjectId of the AVObject to fetch. - // /// The cancellation token. - // /// The AVObject for the given objectId. - // public abstract Task GetAsync(string objectId, CancellationToken cancellationToken); - - // internal object GetConstraint(string key) - // { - // return where == null ? null : where.GetOrDefault(key, null); - // } - - // /// - // /// 构建查询字符串 - // /// - // /// 是否包含 ClassName - // /// - // public IDictionary BuildParameters(bool includeClassName = false) - // { - // Dictionary result = new Dictionary(); - // if (where != null) - // { - // result["where"] = PointerOrLocalIdEncoder.Instance.Encode(where); - // } - // if (orderBy != null) - // { - // result["order"] = string.Join(",", orderBy.ToArray()); - // } - // if (skip != null) - // { - // result["skip"] = skip.Value; - // } - // if (limit != null) - // { - // result["limit"] = limit.Value; - // } - // if (includes != null) - // { - // result["include"] = string.Join(",", includes.ToArray()); - // } - // if (selectedKeys != null) - // { - // result["keys"] = string.Join(",", selectedKeys.ToArray()); - // } - // if (includeClassName) - // { - // result["className"] = className; - // } - // if (redirectClassNameForKey != null) - // { - // result["redirectClassNameForKey"] = redirectClassNameForKey; - // } - // return result; - // } - - // private string RegexQuote(string input) - // { - // return "\\Q" + input.Replace("\\E", "\\E\\\\E\\Q") + "\\E"; - // } - - // private string GetRegexOptions(Regex regex, string modifiers) - // { - // string result = modifiers ?? ""; - // if (regex.Options.HasFlag(RegexOptions.IgnoreCase) && !modifiers.Contains("i")) - // { - // result += "i"; - // } - // if (regex.Options.HasFlag(RegexOptions.Multiline) && !modifiers.Contains("m")) - // { - // result += "m"; - // } - // return result; - // } - - // private IDictionary EncodeRegex(Regex regex, string modifiers) - // { - // var options = GetRegexOptions(regex, modifiers); - // var dict = new Dictionary(); - // dict["$regex"] = regex.ToString(); - // if (!string.IsNullOrEmpty(options)) - // { - // dict["$options"] = options; - // } - // return dict; - // } - - // /// - // /// Serves as the default hash function. - // /// - // /// A hash code for the current object. - // public override int GetHashCode() - // { - // // TODO (richardross): Implement this. - // return 0; - // } - //} -} diff --git a/Storage/Source/Public/LeaderBoard/AVLeaderboard.cs b/Storage/Source/Public/LeaderBoard/AVLeaderboard.cs deleted file mode 100644 index 8713c1b..0000000 --- a/Storage/Source/Public/LeaderBoard/AVLeaderboard.cs +++ /dev/null @@ -1,479 +0,0 @@ -using System; -using System.Threading.Tasks; -using System.Collections.Generic; -using LeanCloud.Storage.Internal; -using System.IO; -using System.Text; - -namespace LeanCloud { - /// - /// 排行榜顺序 - /// - public enum AVLeaderboardOrder { - /// - /// 升序 - /// - ASCENDING, - /// - /// 降序 - /// - DESCENDING - } - - /// - /// 排行榜更新策略 - /// - public enum AVLeaderboardUpdateStrategy { - /// - /// 更好地 - /// - BETTER, - /// - /// 最近的 - /// - LAST, - /// - /// 总和 - /// - SUM, - } - - /// - /// 排行榜刷新频率 - /// - public enum AVLeaderboardVersionChangeInterval { - /// - /// 从不 - /// - NEVER, - /// - /// 每天 - /// - DAY, - /// - /// 每周 - /// - WEEK, - /// - /// 每月 - /// - MONTH - } - - /// - /// 排行榜类 - /// - public class AVLeaderboard { - /// - /// 成绩名字 - /// - /// The name of the statistic. - public string StatisticName { - get; private set; - } - - /// - /// 排行榜顺序 - /// - /// The order. - public AVLeaderboardOrder Order { - get; private set; - } - - /// - /// 排行榜更新策略 - /// - /// The update strategy. - public AVLeaderboardUpdateStrategy UpdateStrategy { - get; private set; - } - - /// - /// 排行榜版本更新频率 - /// - /// The version change intervak. - public AVLeaderboardVersionChangeInterval VersionChangeInterval { - get; private set; - } - - /// - /// 版本号 - /// - /// The version. - public int Version { - get; private set; - } - - /// - /// 下次重置时间 - /// - /// The next reset at. - public DateTime NextResetAt { - get; private set; - } - - /// - /// 创建时间 - /// - /// The created at. - public DateTime CreatedAt { - get; private set; - } - - /// - /// Leaderboard 构造方法 - /// - /// 成绩名称 - AVLeaderboard(string statisticName) { - StatisticName = statisticName; - } - - AVLeaderboard() { - } - - /// - /// 创建排行榜对象 - /// - /// 排行榜对象 - /// 名称 - /// 排序方式 - /// 版本更新频率 - /// 成绩更新策略 - public static Task CreateLeaderboard(string statisticName, - AVLeaderboardOrder order = AVLeaderboardOrder.DESCENDING, - AVLeaderboardUpdateStrategy updateStrategy = AVLeaderboardUpdateStrategy.BETTER, - AVLeaderboardVersionChangeInterval versionChangeInterval = AVLeaderboardVersionChangeInterval.WEEK) { - - if (string.IsNullOrEmpty(statisticName)) { - throw new ArgumentNullException(nameof(statisticName)); - } - var data = new Dictionary { - { "statisticName", statisticName }, - { "order", order.ToString().ToLower() }, - { "versionChangeInterval", versionChangeInterval.ToString().ToLower() }, - { "updateStrategy", updateStrategy.ToString().ToLower() }, - }; - var command = new AVCommand("leaderboard/leaderboards", "POST", data: data); - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).OnSuccess(t => { - try { - var leaderboard = Parse(t.Result.Item2); - return leaderboard; - } catch (Exception e) { - throw new AVException(AVException.ErrorCode.InvalidJSON, e.Message); - } - }); - } - - /// - /// 创建排行榜对象 - /// - /// 排行榜对象 - /// 名称 - public static AVLeaderboard CreateWithoutData(string statisticName) { - if (string.IsNullOrEmpty(statisticName)) { - throw new ArgumentNullException(nameof(statisticName)); - } - return new AVLeaderboard(statisticName); - } - - /// - /// 获取排行榜对象 - /// - /// 排行榜对象 - /// 名称 - public static Task GetLeaderboard(string statisticName) { - return CreateWithoutData(statisticName).Fetch(); - } - - /// - /// 更新用户成绩 - /// - /// 更新的成绩 - /// 用户 - /// 成绩 - /// 是否强行覆盖 - public static Task> UpdateStatistics(AVUser user, Dictionary statistics, bool overwrite = false) { - if (user == null) { - throw new ArgumentNullException(nameof(user)); - } - if (statistics == null || statistics.Count == 0) { - throw new ArgumentNullException(nameof(statistics)); - } - var data = new List(); - foreach (var statistic in statistics) { - var kv = new Dictionary { - { "statisticName", statistic.Key }, - { "statisticValue", statistic.Value }, - }; - data.Add(kv); - } - var path = string.Format("leaderboard/users/{0}/statistics", user.ObjectId); - if (overwrite) { - path = string.Format("{0}?overwrite=1", path); - } - var dataStr = Json.Encode(data); - var dataStream = new MemoryStream(Encoding.UTF8.GetBytes(dataStr)); - var command = new AVCommand(path, "POST", contentType: "application/json", sessionToken: user.SessionToken, stream: dataStream); - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).OnSuccess(t => { - try { - List statisticList = new List(); - List list = t.Result.Item2["results"] as List; - foreach (object obj in list) { - statisticList.Add(AVStatistic.Parse(obj as IDictionary)); - } - return statisticList; - } catch (Exception e) { - throw new AVException(AVException.ErrorCode.InvalidJSON, e.Message); - } - }); - } - - /// - /// 获取用户成绩 - /// - /// 成绩列表 - /// 用户 - /// 名称列表 - public static Task> GetStatistics(AVUser user, List statisticNames = null) { - if (user == null) { - throw new ArgumentNullException(nameof(user)); - } - var path = string.Format("leaderboard/users/{0}/statistics", user.ObjectId); - if (statisticNames != null && statisticNames.Count > 0) { - var names = string.Join(",", statisticNames.ToArray()); - path = string.Format("{0}?statistics={1}", path, names); - } - var sessionToken = AVUser.CurrentUser?.SessionToken; - var command = new AVCommand(path, "GET", sessionToken, data: null); - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).OnSuccess(t => { - try { - List statisticList = new List(); - List list = t.Result.Item2["results"] as List; - foreach (object obj in list) { - statisticList.Add(AVStatistic.Parse(obj as IDictionary)); - } - return statisticList; - } catch (Exception e) { - throw new AVException(AVException.ErrorCode.InvalidJSON, e.Message); - } - }); - } - - /// - /// 删除用户成绩 - /// - /// 用户 - /// 名称列表 - public static Task DeleteStatistics(AVUser user, List statisticNames) { - if (user == null) { - throw new ArgumentNullException(nameof(user)); - } - if (statisticNames == null || statisticNames.Count == 0) { - throw new ArgumentNullException(nameof(statisticNames)); - } - var path = string.Format("leaderboard/users/{0}/statistics", user.ObjectId); - var names = string.Join(",", statisticNames.ToArray()); - path = string.Format("{0}?statistics={1}", path, names); - var command = new AVCommand(path, "DELETE", sessionToken: user.SessionToken, data: null); - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command); - } - - /// - /// 获取排行榜历史数据 - /// - /// 排行榜归档列表 - /// 跳过数量 - /// 分页数量 - public Task> GetArchives(int skip = 0, int limit = 10) { - var path = string.Format("leaderboard/leaderboards/{0}/archives", StatisticName); - path = string.Format("{0}?skip={1}&limit={2}", path, skip, limit); - var command = new AVCommand(path, "GET", data: null); - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).OnSuccess(t => { - List archives = new List(); - List list = t.Result.Item2["results"] as List; - foreach (object obj in list) { - archives.Add(AVLeaderboardArchive.Parse(obj as IDictionary)); - } - return archives; - }); - } - - /// - /// 获取排行榜结果 - /// - /// 排名列表 - public Task> GetResults(int version = -1, int skip = 0, int limit = 10, List selectUserKeys = null, - List includeStatistics = null) { - return GetResults(null, version, skip, limit, selectUserKeys, includeStatistics); - } - - /// - /// 获取用户及附近的排名 - /// - /// 排名列表 - /// 用户 - /// 版本号 - /// 跳过数量 - /// 分页数量 - /// 包含的玩家的字段列表 - /// 包含的其他排行榜名称 - public Task> GetResultsAroundUser(int version = -1, int skip = 0, int limit = 10, - List selectUserKeys = null, - List includeStatistics = null) { - return GetResults(AVUser.CurrentUser, version, skip, limit, selectUserKeys, includeStatistics); - } - - Task> GetResults(AVUser user, - int version, int skip, int limit, - List selectUserKeys, - List includeStatistics) { - - var path = string.Format("leaderboard/leaderboards/{0}/ranks", StatisticName); - if (user != null) { - path = string.Format("{0}/{1}", path, user.ObjectId); - } - path = string.Format("{0}?skip={1}&limit={2}", path, skip, limit); - if (version != -1) { - path = string.Format("{0}&version={1}", path, version); - } - if (selectUserKeys != null) { - var keys = string.Join(",", selectUserKeys.ToArray()); - path = string.Format("{0}&includeUser={1}", path, keys); - } - if (includeStatistics != null) { - var statistics = string.Join(",", includeStatistics.ToArray()); - path = string.Format("{0}&includeStatistics={1}", path, statistics); - } - var command = new AVCommand(path, "GET", data: null); - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).OnSuccess(t => { - try { - List rankingList = new List(); - List list = t.Result.Item2["results"] as List; - foreach (object obj in list) { - rankingList.Add(AVRanking.Parse(obj as IDictionary)); - } - return rankingList; - } catch (Exception e) { - throw new AVException(AVException.ErrorCode.InvalidJSON, e.Message); - } - }); - } - - /// - /// 设置更新策略 - /// - /// 排行榜对象 - /// 更新策略 - public Task UpdateUpdateStrategy(AVLeaderboardUpdateStrategy updateStrategy) { - var data = new Dictionary { - { "updateStrategy", updateStrategy.ToString().ToLower() } - }; - return Update(data).OnSuccess(t => { - UpdateStrategy = (AVLeaderboardUpdateStrategy)Enum.Parse(typeof(AVLeaderboardUpdateStrategy), t.Result["updateStrategy"].ToString().ToUpper()); - return this; - }); - } - - /// - /// 设置版本更新频率 - /// - /// 排行榜对象 - /// 版本更新频率 - public Task UpdateVersionChangeInterval(AVLeaderboardVersionChangeInterval versionChangeInterval) { - var data = new Dictionary { - { "versionChangeInterval", versionChangeInterval.ToString().ToLower() } - }; - return Update(data).OnSuccess(t => { - VersionChangeInterval = (AVLeaderboardVersionChangeInterval)Enum.Parse(typeof(AVLeaderboardVersionChangeInterval), t.Result["versionChangeInterval"].ToString().ToUpper()); - return this; - }); - } - - Task> Update(Dictionary data) { - var path = string.Format("leaderboard/leaderboards/{0}", StatisticName); - var command = new AVCommand(path, "PUT", data: data); - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).OnSuccess(t => { - return t.Result.Item2; - }); - } - - /// - /// 拉取排行榜数据 - /// - /// 排行榜对象 - public Task Fetch() { - var path = string.Format("leaderboard/leaderboards/{0}", StatisticName); - var command = new AVCommand(path, "GET", data: null); - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).OnSuccess(t => { - try { - // 反序列化 Leaderboard 对象 - var leaderboard = Parse(t.Result.Item2); - return leaderboard; - } catch (Exception e) { - throw new AVException(AVException.ErrorCode.InvalidJSON, e.Message); - } - }); - } - - /// - /// 重置排行榜 - /// - /// 排行榜对象 - public Task Reset() { - var path = string.Format("leaderboard/leaderboards/{0}/incrementVersion", StatisticName); - var command = new AVCommand(path, "PUT", data: null); - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command).OnSuccess(t => { - try { - Init(t.Result.Item2); - return this; - } catch (Exception e) { - throw new AVException(AVException.ErrorCode.InvalidJSON, e.Message); - } - }); - } - - /// - /// 销毁排行榜 - /// - public Task Destroy() { - var path = string.Format("leaderboard/leaderboards/{0}", StatisticName); - var command = new AVCommand(path, "DELETE", data: null); - return AVPlugins.Instance.CommandRunner.RunCommandAsync(command); - } - - static AVLeaderboard Parse(IDictionary data) { - if (data == null) { - throw new ArgumentNullException(nameof(data)); - } - var leaderboard = new AVLeaderboard(); - leaderboard.Init(data); - return leaderboard; - } - - void Init(IDictionary data) { - if (data == null) { - throw new ArgumentNullException(nameof(data)); - } - object nameObj; - if (data.TryGetValue("statisticName", out nameObj)) { - StatisticName = nameObj.ToString(); - } - object orderObj; - if (data.TryGetValue("order", out orderObj)) { - Order = (AVLeaderboardOrder)Enum.Parse(typeof(AVLeaderboardOrder), orderObj.ToString().ToUpper()); - } - object strategyObj; - if (data.TryGetValue("updateStrategy", out strategyObj)) { - UpdateStrategy = (AVLeaderboardUpdateStrategy)Enum.Parse(typeof(AVLeaderboardUpdateStrategy), strategyObj.ToString().ToUpper()); - } - object intervalObj; - if (data.TryGetValue("versionChangeInterval", out intervalObj)) { - VersionChangeInterval = (AVLeaderboardVersionChangeInterval)Enum.Parse(typeof(AVLeaderboardVersionChangeInterval), intervalObj.ToString().ToUpper()); - } - object versionObj; - if (data.TryGetValue("version", out versionObj)) { - Version = int.Parse(versionObj.ToString()); - } - } - } -} diff --git a/Storage/Source/Public/LeaderBoard/AVLeaderboardArchive.cs b/Storage/Source/Public/LeaderBoard/AVLeaderboardArchive.cs deleted file mode 100644 index a9df41f..0000000 --- a/Storage/Source/Public/LeaderBoard/AVLeaderboardArchive.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.Collections.Generic; -using LeanCloud.Storage.Internal; - -namespace LeanCloud { - /// - /// 归档的排行榜 - /// - public class AVLeaderboardArchive { - /// - /// 名称 - /// - /// The name of the statistic. - public string StatisticName { - get; internal set; - } - - /// - /// 版本号 - /// - /// The version. - public int Version { - get; internal set; - } - - /// - /// 状态 - /// - /// The status. - public string Status { - get; internal set; - } - - /// - /// 下载地址 - /// - /// The URL. - public string Url { - get; internal set; - } - - /// - /// 激活时间 - /// - /// The activated at. - public DateTime ActivatedAt { - get; internal set; - } - - /// - /// 归档时间 - /// - /// The deactivated at. - public DateTime DeactivatedAt { - get; internal set; - } - - AVLeaderboardArchive() { - } - - internal static AVLeaderboardArchive Parse(IDictionary data) { - if (data == null) { - throw new ArgumentNullException(nameof(data)); - } - AVLeaderboardArchive archive = new AVLeaderboardArchive { - StatisticName = data["statisticName"].ToString(), - Version = int.Parse(data["version"].ToString()), - Status = data["status"].ToString(), - Url = data["url"].ToString(), - ActivatedAt = (DateTime)AVDecoder.Instance.Decode(data["activatedAt"]), - DeactivatedAt = (DateTime)AVDecoder.Instance.Decode(data["activatedAt"]) - }; - return archive; - } - } -} diff --git a/Storage/Source/Public/LeaderBoard/AVRanking.cs b/Storage/Source/Public/LeaderBoard/AVRanking.cs deleted file mode 100644 index 6d5fa90..0000000 --- a/Storage/Source/Public/LeaderBoard/AVRanking.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using System.Collections.Generic; -using LeanCloud.Storage.Internal; - -namespace LeanCloud { - /// - /// 排名类 - /// - public class AVRanking { - /// - /// 名次 - /// - /// The rank. - public int Rank { - get; private set; - } - - /// - /// 用户 - /// - /// The user. - public AVUser User { - get; private set; - } - - public string StatisticName { - get; private set; - } - - /// - /// 分数 - /// - /// The value. - public double Value { - get; private set; - } - - /// - /// 成绩 - /// - /// The included statistics. - public List IncludedStatistics { - get; private set; - } - - AVRanking() { - } - - internal static AVRanking Parse(IDictionary data) { - if (data == null) { - throw new ArgumentNullException(nameof(data)); - } - var ranking = new AVRanking { - Rank = int.Parse(data["rank"].ToString()), - User = AVDecoder.Instance.Decode(data["user"]) as AVUser, - StatisticName = data["statisticName"].ToString(), - Value = double.Parse(data["statisticValue"].ToString()) - }; - object statisticsObj; - if (data.TryGetValue("statistics", out statisticsObj)) { - ranking.IncludedStatistics = new List(); - var statisticsObjList = statisticsObj as List; - foreach (object statisticObj in statisticsObjList) { - var statistic = AVStatistic.Parse(statisticObj as IDictionary); - ranking.IncludedStatistics.Add(statistic); - } - } - - return ranking; - } - } -} diff --git a/Storage/Source/Public/LeaderBoard/AVStatistic.cs b/Storage/Source/Public/LeaderBoard/AVStatistic.cs deleted file mode 100644 index 6d4fdcb..0000000 --- a/Storage/Source/Public/LeaderBoard/AVStatistic.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace LeanCloud { - /// - /// 成绩类 - /// - public class AVStatistic { - /// - /// 排行榜名称 - /// - /// The name. - public string Name { - get; private set; - } - - /// - /// 成绩值 - /// - /// The value. - public double Value { - get; private set; - } - - /// - /// 排行榜版本 - /// - /// The version. - public int Version { - get; internal set; - } - - public AVStatistic(string name, double value) { - Name = name; - Value = value; - } - - AVStatistic() { } - - internal static AVStatistic Parse(IDictionary data) { - if (data == null) { - throw new ArgumentNullException(nameof(data)); - } - AVStatistic statistic = new AVStatistic { - Name = data["statisticName"].ToString(), - Value = double.Parse(data["statisticValue"].ToString()), - Version = int.Parse(data["version"].ToString()) - }; - return statistic; - } - } -} diff --git a/Storage/Source/Public/Unity/AVInitializeBehaviour.cs b/Storage/Source/Public/Unity/AVInitializeBehaviour.cs deleted file mode 100644 index 1012c30..0000000 --- a/Storage/Source/Public/Unity/AVInitializeBehaviour.cs +++ /dev/null @@ -1,75 +0,0 @@ -using LeanCloud.Storage.Internal; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using UnityEngine; - -namespace LeanCloud -{ - public class AVCoreExtensions : MonoBehaviour - { - [RuntimeInitializeOnLoadMethod] - static void OnRuntimeMethodLoad() { - var go = new GameObject { - name = "AVCoreExtensions" - }; - DontDestroyOnLoad(go); - var avce = go.AddComponent(); - - Dispatcher.Instance.GameObject = go; - - // Kick off the dispatcher. - avce.StartCoroutine(Dispatcher.Instance.DispatcherCoroutine); - - AVInitializeBehaviour.IsWebPlayer = Application.platform == RuntimePlatform.WebGLPlayer; - } - } - - /// - /// Mandatory MonoBehaviour for scenes that use LeanCloud. Set the application ID and .NET key - /// in the editor. - /// - // TODO (hallucinogen): somehow because of Push, we need this class to be added in a GameObject - // called `AVInitializeBehaviour`. We might want to fix this. - public class AVInitializeBehaviour : MonoBehaviour - { - [SerializeField] - /// - /// The LeanCloud applicationId used in this app. You can get this value from the LeanCloud website. - /// - public string applicationID; - - - [SerializeField] - /// - /// The LeanCloud applicationKey used in this app. You can get this value from the LeanCloud website. - /// - public string applicationKey; - - [SerializeField] - /// - /// The service region. - /// - public AVClient.Configuration.AVRegion region; - - - [SerializeField] - /// - /// Use this uri as cloud function server host. This is used for local development. - /// - public string engineServer; - - [SerializeField] - /// - /// Whether use production stage to process request or not. - /// - public bool useProduction = true; - - /// - /// Gets or sets a value indicating whether this is web player. - /// - /// true if is web player; otherwise, false. - public static bool IsWebPlayer { get; set; } - } -} diff --git a/Storage/Source/Public/Utilities/Conversion.cs b/Storage/Source/Public/Utilities/Conversion.cs deleted file mode 100644 index f802f5f..0000000 --- a/Storage/Source/Public/Utilities/Conversion.cs +++ /dev/null @@ -1,138 +0,0 @@ -using System; -using System.Collections.Generic; -using LeanCloud.Storage.Internal; - -namespace LeanCloud.Utilities -{ - /// - /// A set of utilities for converting generic types between each other. - /// - public static class Conversion - { - /// - /// Converts a value to the requested type -- coercing primitives to - /// the desired type, wrapping lists and dictionaries appropriately, - /// or else returning null. - /// - /// This should be used on any containers that might be coming from a - /// user to normalize the collection types. Collection types coming from - /// JSON deserialization can be safely assumed to be lists or dictionaries of - /// objects. - /// - public static T As(object value) where T : class - { - return ConvertTo(value) as T; - } - - /// - /// Converts a value to the requested type -- coercing primitives to - /// the desired type, wrapping lists and dictionaries appropriately, - /// or else throwing an exception. - /// - /// This should be used on any containers that might be coming from a - /// user to normalize the collection types. Collection types coming from - /// JSON deserialization can be safely assumed to be lists or dictionaries of - /// objects. - /// - public static T To(object value) - { - return (T)ConvertTo(value); - } - - /// - /// Converts a value to the requested type -- coercing primitives to - /// the desired type, wrapping lists and dictionaries appropriately, - /// or else passing the object along to the caller unchanged. - /// - /// This should be used on any containers that might be coming from a - /// user to normalize the collection types. Collection types coming from - /// JSON deserialization can be safely assumed to be lists or dictionaries of - /// objects. - /// - internal static object ConvertTo(object value) - { - if (value is T || value == null) - { - return value; - } - - if (ReflectionHelpers.IsPrimitive(typeof(T))) - { - return (T)Convert.ChangeType(value, typeof(T)); - } - - if (ReflectionHelpers.IsConstructedGenericType(typeof(T))) - { - // Add lifting for nullables. Only supports conversions between primitives. - if (ReflectionHelpers.IsNullable(typeof(T))) - { - var innerType = ReflectionHelpers.GetGenericTypeArguments(typeof(T))[0]; - if (ReflectionHelpers.IsPrimitive(innerType)) - { - return (T)Convert.ChangeType(value, innerType); - } - } - Type listType = GetInterfaceType(value.GetType(), typeof(IList<>)); - var la = typeof(T).GetGenericTypeDefinition(); - var ilb = typeof(IList<>); - var lb = typeof(List<>); - if (listType != null && - (la == ilb || la == lb)) - { - var wrapperType = typeof(FlexibleListWrapper<,>) - .MakeGenericType(ReflectionHelpers.GetGenericTypeArguments(typeof(T))[0], - ReflectionHelpers.GetGenericTypeArguments(listType)[0]); - return Activator.CreateInstance(wrapperType, value); - } - Type dictType = GetInterfaceType(value.GetType(), typeof(IDictionary<,>)); - var da = typeof(T).GetGenericTypeDefinition(); - var db = typeof(IDictionary<,>); - if (dictType != null && - da == db) - { - var wrapperType = typeof(FlexibleDictionaryWrapper<,>) - .MakeGenericType(ReflectionHelpers.GetGenericTypeArguments(typeof(T))[1], - ReflectionHelpers.GetGenericTypeArguments(dictType)[1]); - return Activator.CreateInstance(wrapperType, value); - } - } - - return value; - } - - /// - /// Holds a dictionary that maps a cache of interface types for related concrete types. - /// The lookup is slow the first time for each type because it has to enumerate all interface - /// on the object type, but made fast by the cache. - /// - /// The map is: - /// (object type, generic interface type) => constructed generic type - /// - private static readonly Dictionary, Type> interfaceLookupCache = - new Dictionary, Type>(); - private static Type GetInterfaceType(Type objType, Type genericInterfaceType) - { - // Side note: It so sucks to have to do this. What a piece of crap bit of code - // Unfortunately, .NET doesn't provide any of the right hooks to do this for you - // *sigh* - if (ReflectionHelpers.IsConstructedGenericType(genericInterfaceType)) - { - genericInterfaceType = genericInterfaceType.GetGenericTypeDefinition(); - } - var cacheKey = new Tuple(objType, genericInterfaceType); - if (interfaceLookupCache.ContainsKey(cacheKey)) - { - return interfaceLookupCache[cacheKey]; - } - foreach (var type in ReflectionHelpers.GetInterfaces(objType)) - { - if (ReflectionHelpers.IsConstructedGenericType(type) && - type.GetGenericTypeDefinition() == genericInterfaceType) - { - return interfaceLookupCache[cacheKey] = type; - } - } - return null; - } - } -} diff --git a/Storage/Storage.PCL/Properties/AssemblyInfo.cs b/Storage/Storage.PCL/Properties/AssemblyInfo.cs deleted file mode 100644 index 5ec291d..0000000 --- a/Storage/Storage.PCL/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. - -[assembly: AssemblyTitle("Storage.PCL")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("")] -[assembly: AssemblyCopyright("${AuthorCopyright}")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. - -[assembly: AssemblyVersion("1.0.*")] - -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. - -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] diff --git a/Storage/Storage.PCL/Storage.PCL.csproj b/Storage/Storage.PCL/Storage.PCL.csproj deleted file mode 100644 index 99cc1ce..0000000 --- a/Storage/Storage.PCL/Storage.PCL.csproj +++ /dev/null @@ -1,363 +0,0 @@ - - - - Debug - AnyCPU - {659D19F0-9A40-42C0-886C-555E64F16848} - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - Library - Storage.PCL - Storage.PCL - v4.5 - Profile111 - 0.1.0 - - - true - full - false - bin\Debug - DEBUG; - prompt - 4 - - - true - bin\Release - prompt - 4 - - - - - Internal\IAVCorePlugins.cs - - - Internal\AVCorePlugins.cs - - - Internal\AppRouter\AppRouterController.cs - - - Internal\AppRouter\AppRouterState.cs - - - Internal\AppRouter\IAppRouterController.cs - - - Internal\Authentication\IAVAuthenticationProvider.cs - - - Internal\Cloud\Controller\IAVCloudCodeController.cs - - - Internal\Cloud\Controller\AVCloudCodeController.cs - - - Internal\Command\IAVCommandRunner.cs - - - Internal\Command\AVCommand.cs - - - Internal\Command\AVCommandRunner.cs - - - Internal\Config\Controller\IAVConfigController.cs - - - Internal\Config\Controller\IAVCurrentConfigController.cs - - - Internal\Config\Controller\AVConfigController.cs - - - Internal\Config\Controller\AVCurrentConfigController.cs - - - Internal\Encoding\NoObjectsEncoder.cs - - - Internal\Encoding\AVDecoder.cs - - - Internal\Encoding\AVEncoder.cs - - - Internal\Encoding\AVObjectCoder.cs - - - Internal\Encoding\PointerOrLocalIdEncoder.cs - - - Internal\File\Controller\AWSS3FileController.cs - - - Internal\File\Controller\IAVFileController.cs - - - Internal\File\Controller\AVFileController.cs - - - Internal\File\Controller\QCloudCosFileController.cs - - - Internal\File\Controller\QiniuFileController.cs - - - Internal\File\Cryptography\MD5\MD5.cs - - - Internal\File\Cryptography\SHA1\SHA1CryptoServiceProvider.cs - - - Internal\File\State\FileState.cs - - - Internal\HttpClient\HttpRequest.cs - - - Internal\HttpClient\IHttpClient.cs - - - Internal\HttpClient\Portable\HttpClient.Portable.cs - - - Internal\InstallationId\Controller\IInstallationIdController.cs - - - Internal\InstallationId\Controller\InstallationIdController.cs - - - Internal\Object\Controller\IAVObjectController.cs - - - Internal\Object\Controller\IAVObjectCurrentController.cs - - - Internal\Object\Controller\AVObjectController.cs - - - Internal\Object\State\IObjectState.cs - - - Internal\Object\State\MutableObjectState.cs - - - Internal\Object\Subclassing\IObjectSubclassingController.cs - - - Internal\Object\Subclassing\ObjectSubclassInfo.cs - - - Internal\Object\Subclassing\ObjectSubclassingController.cs - - - Internal\Operation\IAVFieldOperation.cs - - - Internal\Operation\AVAddOperation.cs - - - Internal\Operation\AVAddUniqueOperation.cs - - - Internal\Operation\AVDeleteOperation.cs - - - Internal\Operation\AVFieldOperations.cs - - - Internal\Operation\AVIncrementOperation.cs - - - Internal\Operation\AVRelationOperation.cs - - - Internal\Operation\AVRemoveOperation.cs - - - Internal\Operation\AVSetOperation.cs - - - Internal\Query\Controller\IAVQueryController.cs - - - Internal\Query\Controller\AVQueryController.cs - - - Internal\Session\Controller\IAVSessionController.cs - - - Internal\Session\Controller\AVSessionController.cs - - - Internal\Storage\IStorageController.cs - - - Internal\Storage\Portable\StorageController.cs - - - Internal\User\Controller\IAVCurrentUserController.cs - - - Internal\User\Controller\IAVUserController.cs - - - Internal\User\Controller\AVCurrentUserController.cs - - - Internal\User\Controller\AVUserController.cs - - - Internal\Utilities\FlexibleDictionaryWrapper.cs - - - Internal\Utilities\FlexibleListWrapper.cs - - - Internal\Utilities\IJsonConvertible.cs - - - Internal\Utilities\IdentityEqualityComparer.cs - - - Internal\Utilities\InternalExtensions.cs - - - Internal\Utilities\Json.cs - - - Internal\Utilities\LockSet.cs - - - Internal\Utilities\AVConfigExtensions.cs - - - Internal\Utilities\AVFileExtensions.cs - - - Internal\Utilities\AVObjectExtensions.cs - - - Internal\Utilities\AVQueryExtensions.cs - - - Internal\Utilities\AVRelationExtensions.cs - - - Internal\Utilities\AVSessionExtensions.cs - - - Internal\Utilities\AVUserExtensions.cs - - - Internal\Utilities\ReflectionHelpers.cs - - - Internal\Utilities\SynchronizedEventHandler.cs - - - Internal\Utilities\TaskQueue.cs - - - Internal\Utilities\XamarinAttributes.cs - - - Public\AVACL.cs - - - Public\AVClassNameAttribute.cs - - - Public\AVClient.cs - - - Public\AVCloud.cs - - - Public\AVConfig.cs - - - Public\AVDownloadProgressEventArgs.cs - - - Public\AVException.cs - - - Public\AVExtensions.cs - - - Public\AVFieldNameAttribute.cs - - - Public\AVFile.cs - - - Public\AVGeoDistance.cs - - - Public\AVGeoPoint.cs - - - Public\AVObject.cs - - - Public\AVQuery.cs - - - Public\AVQueryExtensions.cs - - - Public\AVRelation.cs - - - Public\AVRole.cs - - - Public\AVSession.cs - - - Public\AVStatus.cs - - - Public\AVUploadProgressEventArgs.cs - - - Public\AVUser.cs - - - Public\AVUserAuthDataLogInOption.cs - - - Public\IAVQuery.cs - - - Public\LeaderBoard\AVLeaderboard.cs - - - Public\LeaderBoard\AVLeaderboardArchive.cs - - - Public\LeaderBoard\AVRanking.cs - - - Public\LeaderBoard\AVStatistic.cs - - - Public\Utilities\Conversion.cs - - - - - - - - ..\..\packages\PCLStorage.1.0.2\lib\portable-net45+wp8+wpa81+win8+monoandroid+monotouch+Xamarin.iOS+Xamarin.Mac\PCLStorage.Abstractions.dll - - - ..\..\packages\PCLStorage.1.0.2\lib\portable-net45+wp8+wpa81+win8+monoandroid+monotouch+Xamarin.iOS+Xamarin.Mac\PCLStorage.dll - - - - \ No newline at end of file diff --git a/Storage/Storage.PCL/packages.config b/Storage/Storage.PCL/packages.config deleted file mode 100644 index 5306567..0000000 --- a/Storage/Storage.PCL/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/Storage/Storage.Test/Utils.cs b/Storage/Storage.Test/Utils.cs deleted file mode 100644 index 8b93ff2..0000000 --- a/Storage/Storage.Test/Utils.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using LeanCloud; -using LeanCloud.Common; -using NUnit.Framework; - -namespace LeanCloud.Test { - public static class Utils { - public static void InitNorthChina(bool master = false) { - if (master) { - Init("BMYV4RKSTwo8WSqt8q9ezcWF-gzGzoHsz", "pbf6Nk5seyjilexdpyrPwjSp", "https://avoscloud.com", "https://avoscloud.com", "qKH9ryRagHKvXeRRVkiUiHeb"); - } else { - Init("BMYV4RKSTwo8WSqt8q9ezcWF-gzGzoHsz", "pbf6Nk5seyjilexdpyrPwjSp", "https://avoscloud.com", "https://avoscloud.com"); - } - } - - public static void InitEastChina(bool master = false) { - if (master) { - Init("4eTwHdYhMaNBUpl1SrTr7GLC-9Nh9j0Va", "GSD6DtdgGWlWolivN4qhWtlE", "https://4eTwHdYh.api.lncldapi.com", "https://4eTwHdYh.engine.lncldapi.com", "eqEp4n89h4zanWFskDDpIwL4"); - } else { - Init("4eTwHdYhMaNBUpl1SrTr7GLC-9Nh9j0Va", "GSD6DtdgGWlWolivN4qhWtlE", "https://4eTwHdYh.api.lncldapi.com", "https://4eTwHdYh.engine.lncldapi.com"); - } - } - - public static void InitOldEastChina(bool master = false) { - if (master) { - Init("SpT4SjWdvM9TSvCTKk6rqYQ9-9Nh9j0Va", "4NvN2OfdsWFC7qzzNcNS6paS", "https://4eTwHdYh.api.lncldapi.com", "https://4eTwHdYh.engine.lncldapi.com", "eqEp4n89h4zanWFskDDpIwL4"); - } else { - Init("SpT4SjWdvM9TSvCTKk6rqYQ9-9Nh9j0Va", "4NvN2OfdsWFC7qzzNcNS6paS", "https://4eTwHdYh.api.lncldapi.com", "https://4eTwHdYh.engine.lncldapi.com"); - } - } - - public static void InitUS(bool master = false) { - if (master) { - Init("MFAS1GnOyomRLSQYRaxdgdPz-MdYXbMMI", "p42JUxdxb95K5G8187t5ba3l", "https://MFAS1GnO.api.lncldglobal.com", "https://MFAS1GnO.engine.lncldglobal.com", "Ahb1wdFLwMgKwEaEicHRXbCY"); - } else { - Init("MFAS1GnOyomRLSQYRaxdgdPz-MdYXbMMI", "p42JUxdxb95K5G8187t5ba3l", "https://MFAS1GnO.api.lncldglobal.com", "https://MFAS1GnO.engine.lncldglobal.com"); - } - } - - static void Init(string appId, string appKey, string apiServer, string engineServer, string masterKey = null) { - AVClient.Initialize(new AVClient.Configuration { - ApplicationId = appId, - ApplicationKey = appKey, - MasterKey = masterKey, - ApiServer = apiServer, - EngineServer = engineServer - }); - AVClient.UseMasterKey = !string.IsNullOrEmpty(masterKey); - AVClient.HttpLog(TestContext.Out.WriteLine); - } - - internal static void Print(LogLevel level, string info) { - switch (level) { - case LogLevel.Debug: - TestContext.Out.WriteLine($"[DEBUG] {info}"); - break; - case LogLevel.Warn: - TestContext.Out.WriteLine($"[WARNING] {info}"); - break; - case LogLevel.Error: - TestContext.Out.WriteLine($"[ERROR] {info}"); - break; - default: - TestContext.Out.WriteLine(info); - break; - } - } - } -} diff --git a/Storage/Storage.Unity/Properties/AssemblyInfo.cs b/Storage/Storage.Unity/Properties/AssemblyInfo.cs deleted file mode 100644 index 78f7194..0000000 --- a/Storage/Storage.Unity/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. - -[assembly: AssemblyTitle("Storage.Unity")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("")] -[assembly: AssemblyCopyright("${AuthorCopyright}")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. - -[assembly: AssemblyVersion("1.0.*")] - -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. - -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] diff --git a/Storage/Storage.Unity/Storage.Unity.csproj b/Storage/Storage.Unity/Storage.Unity.csproj deleted file mode 100644 index 259956b..0000000 --- a/Storage/Storage.Unity/Storage.Unity.csproj +++ /dev/null @@ -1,368 +0,0 @@ - - - - Debug - AnyCPU - {A0D50BCB-E50E-4AAE-8E7D-24BF5AE33DAC} - Library - Storage.Unity - Storage.Unity - v4.5 - 0.1.0 - - - true - full - false - bin\Debug - DEBUG;UNITY; - prompt - 4 - false - - - true - bin\Release - UNITY; - prompt - 4 - false - - - - - ..\..\Libs\UnityEngine.dll - - - - - - Internal\IAVCorePlugins.cs - - - Internal\AVCorePlugins.cs - - - Internal\AppRouter\AppRouterController.cs - - - Internal\AppRouter\AppRouterState.cs - - - Internal\AppRouter\IAppRouterController.cs - - - Internal\Cloud\Controller\IAVCloudCodeController.cs - - - Internal\Cloud\Controller\AVCloudCodeController.cs - - - Internal\Command\IAVCommandRunner.cs - - - Internal\Command\AVCommand.cs - - - Internal\Command\AVCommandRunner.cs - - - Internal\Config\Controller\IAVConfigController.cs - - - Internal\Config\Controller\IAVCurrentConfigController.cs - - - Internal\Config\Controller\AVConfigController.cs - - - Internal\Config\Controller\AVCurrentConfigController.cs - - - Internal\Dispatcher\Unity\UnityDispatcher.cs - - - Internal\Encoding\NoObjectsEncoder.cs - - - Internal\Encoding\AVDecoder.cs - - - Internal\Encoding\AVEncoder.cs - - - Internal\Encoding\AVObjectCoder.cs - - - Internal\Encoding\PointerOrLocalIdEncoder.cs - - - Internal\File\Controller\AWSS3FileController.cs - - - Internal\File\Controller\IAVFileController.cs - - - Internal\File\Controller\AVFileController.cs - - - Internal\File\Controller\QCloudCosFileController.cs - - - Internal\File\Controller\QiniuFileController.cs - - - Internal\File\Cryptography\MD5\MD5.cs - - - Internal\File\Cryptography\SHA1\SHA1CryptoServiceProvider.cs - - - Internal\File\State\FileState.cs - - - Internal\HttpClient\HttpRequest.cs - - - Internal\HttpClient\IHttpClient.cs - - - Internal\HttpClient\Unity\HttpClient.Unity.cs - - - Internal\InstallationId\Controller\IInstallationIdController.cs - - - Internal\InstallationId\Controller\InstallationIdController.cs - - - Internal\Object\Controller\IAVObjectController.cs - - - Internal\Object\Controller\IAVObjectCurrentController.cs - - - Internal\Object\Controller\AVObjectController.cs - - - Internal\Object\State\IObjectState.cs - - - Internal\Object\State\MutableObjectState.cs - - - Internal\Object\Subclassing\IObjectSubclassingController.cs - - - Internal\Object\Subclassing\ObjectSubclassInfo.cs - - - Internal\Object\Subclassing\ObjectSubclassingController.cs - - - Internal\Operation\IAVFieldOperation.cs - - - Internal\Operation\AVAddOperation.cs - - - Internal\Operation\AVAddUniqueOperation.cs - - - Internal\Operation\AVDeleteOperation.cs - - - Internal\Operation\AVFieldOperations.cs - - - Internal\Operation\AVIncrementOperation.cs - - - Internal\Operation\AVRelationOperation.cs - - - Internal\Operation\AVRemoveOperation.cs - - - Internal\Operation\AVSetOperation.cs - - - Internal\Query\Controller\IAVQueryController.cs - - - Internal\Query\Controller\AVQueryController.cs - - - Internal\Session\Controller\IAVSessionController.cs - - - Internal\Session\Controller\AVSessionController.cs - - - Internal\Storage\IStorageController.cs - - - Internal\Storage\Unity\StorageController.cs - - - Internal\User\Controller\IAVCurrentUserController.cs - - - Internal\User\Controller\IAVUserController.cs - - - Internal\User\Controller\AVCurrentUserController.cs - - - Internal\User\Controller\AVUserController.cs - - - Internal\Utilities\FlexibleDictionaryWrapper.cs - - - Internal\Utilities\FlexibleListWrapper.cs - - - Internal\Utilities\IJsonConvertible.cs - - - Internal\Utilities\IdentityEqualityComparer.cs - - - Internal\Utilities\InternalExtensions.cs - - - Internal\Utilities\Json.cs - - - Internal\Utilities\LockSet.cs - - - Internal\Utilities\AVConfigExtensions.cs - - - Internal\Utilities\AVFileExtensions.cs - - - Internal\Utilities\AVObjectExtensions.cs - - - Internal\Utilities\AVQueryExtensions.cs - - - Internal\Utilities\AVRelationExtensions.cs - - - Internal\Utilities\AVSessionExtensions.cs - - - Internal\Utilities\AVUserExtensions.cs - - - Internal\Utilities\ReflectionHelpers.cs - - - Internal\Utilities\SynchronizedEventHandler.cs - - - Internal\Utilities\TaskQueue.cs - - - Internal\Utilities\XamarinAttributes.cs - - - Public\AVACL.cs - - - Public\AVClassNameAttribute.cs - - - Public\AVClient.cs - - - Public\AVCloud.cs - - - Public\AVConfig.cs - - - Public\AVDownloadProgressEventArgs.cs - - - Public\AVException.cs - - - Public\AVExtensions.cs - - - Public\AVFieldNameAttribute.cs - - - Public\AVFile.cs - - - Public\AVGeoDistance.cs - - - Public\AVGeoPoint.cs - - - Public\AVObject.cs - - - Public\AVQuery.cs - - - Public\AVQueryExtensions.cs - - - Public\AVRelation.cs - - - Public\AVRole.cs - - - Public\AVSession.cs - - - Public\AVStatus.cs - - - Public\AVUploadProgressEventArgs.cs - - - Public\AVUser.cs - - - Public\AVUserAuthDataLogInOption.cs - - - Public\IAVQuery.cs - - - Public\LeaderBoard\AVLeaderboard.cs - - - Public\LeaderBoard\AVLeaderboardArchive.cs - - - Public\LeaderBoard\AVRanking.cs - - - Public\LeaderBoard\AVStatistic.cs - - - Public\Unity\AVInitializeBehaviour.cs - - - Public\Utilities\Conversion.cs - - - Internal\Authentication\IAVAuthenticationProvider.cs - - - - - - - \ No newline at end of file diff --git a/Storage/Storage/Storage.csproj b/Storage/Storage.csproj similarity index 85% rename from Storage/Storage/Storage.csproj rename to Storage/Storage.csproj index 5ae4643..7793a5f 100644 --- a/Storage/Storage/Storage.csproj +++ b/Storage/Storage.csproj @@ -4,6 +4,7 @@ netstandard2.0 0.1.0 LeanCloud.Storage + true @@ -12,9 +13,6 @@ - - - @@ -24,4 +22,7 @@ + + + diff --git a/Storage/Storage/Internal_/AVCorePlugins.cs b/Storage/Storage/Internal_/AVCorePlugins.cs deleted file mode 100644 index 3ae2718..0000000 --- a/Storage/Storage/Internal_/AVCorePlugins.cs +++ /dev/null @@ -1,170 +0,0 @@ -using LeanCloud.Common; - -namespace LeanCloud.Storage.Internal { - public class AVPlugins { - private static readonly object instanceMutex = new object(); - private static AVPlugins instance; - public static AVPlugins Instance { - get { - lock (instanceMutex) { - instance = instance ?? new AVPlugins(); - return instance; - } - } - } - - private readonly object mutex = new object(); - - #region Server Controllers - - private AppRouterController appRouterController; - private AVCommandRunner commandRunner; - - private AVCloudCodeController cloudCodeController; - private AVFileController fileController; - private AVQueryController queryController; - private AVUserController userController; - private ObjectSubclassingController subclassingController; - - #endregion - - #region Current Instance Controller - - private InstallationIdController installationIdController; - - #endregion - - public void Reset() { - lock (mutex) { - AppRouterController = null; - CommandRunner = null; - - CloudCodeController = null; - FileController = null; - UserController = null; - SubclassingController = null; - - InstallationIdController = null; - } - } - - public AppRouterController AppRouterController { - get { - lock (mutex) { - var conf = AVClient.CurrentConfiguration; - appRouterController = appRouterController ?? new AppRouterController(conf.ApplicationId, conf.ApiServer); - return appRouterController; - } - } - set { - lock (mutex) { - appRouterController = value; - } - } - } - - public AVCommandRunner CommandRunner { - get { - lock (mutex) { - commandRunner = commandRunner ?? new AVCommandRunner(); - return commandRunner; - } - } - set { - lock (mutex) { - commandRunner = value; - } - } - } - - public AVCloudCodeController CloudCodeController { - get { - lock (mutex) { - cloudCodeController = cloudCodeController ?? new AVCloudCodeController(); - return cloudCodeController; - } - } - set { - lock (mutex) { - cloudCodeController = value; - } - } - } - - public AVFileController FileController { - get { - if (fileController != null) { - return fileController; - } - fileController = new AVFileController(); - return fileController; - } - set { - lock (mutex) { - fileController = value; - } - } - } - - public AVQueryController QueryController { - get { - lock (mutex) { - if (queryController == null) { - queryController = new AVQueryController(); - } - return queryController; - } - } - set { - lock (mutex) { - queryController = value; - } - } - } - - public AVUserController UserController { - get { - lock (mutex) { - userController = userController ?? new AVUserController(); - return userController; - } - } - set { - lock (mutex) { - userController = value; - } - } - } - - public ObjectSubclassingController SubclassingController { - get { - lock (mutex) { - if (subclassingController == null) { - subclassingController = new ObjectSubclassingController(); - //subclassingController.AddRegisterHook(typeof(AVUser), () => CurrentUserController.ClearFromMemory()); - } - return subclassingController; - } - } - set { - lock (mutex) { - subclassingController = value; - } - } - } - - public InstallationIdController InstallationIdController { - get { - lock (mutex) { - installationIdController = installationIdController ?? new InstallationIdController(); - return installationIdController; - } - } - set { - lock (mutex) { - installationIdController = value; - } - } - } - } -} diff --git a/Storage/Storage/Internal_/Cloud/Controller/AVCloudCodeController.cs b/Storage/Storage/Internal_/Cloud/Controller/AVCloudCodeController.cs deleted file mode 100644 index bc6c063..0000000 --- a/Storage/Storage/Internal_/Cloud/Controller/AVCloudCodeController.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using LeanCloud.Utilities; -using System.Net.Http; - -namespace LeanCloud.Storage.Internal { - public class AVCloudCodeController { - public async Task CallFunctionAsync(string name, - IDictionary parameters, - CancellationToken cancellationToken) { - var command = new EngineCommand { - Path = $"functions/{Uri.EscapeUriString(name)}", - Method = HttpMethod.Post, - Content = parameters ?? new Dictionary() - }; - var data = await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command, cancellationToken); - var decoded = AVDecoder.Instance.Decode(data.Item2) as IDictionary; - if (!decoded.ContainsKey("result")) { - return default; - } - return Conversion.To(decoded["result"]); - } - - public async Task RPCFunction(string name, IDictionary parameters, CancellationToken cancellationToken) { - var command = new EngineCommand { - Path = $"call/{Uri.EscapeUriString(name)}", - Method = HttpMethod.Post, - Content = parameters ?? new Dictionary() - }; - var data = await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command, cancellationToken); - var decoded = AVDecoder.Instance.Decode(data.Item2) as IDictionary; - if (!decoded.ContainsKey("result")) { - return default; - } - return Conversion.To(decoded["result"]); - } - } -} diff --git a/Storage/Storage/Internal_/Command/AVCommand.cs b/Storage/Storage/Internal_/Command/AVCommand.cs deleted file mode 100644 index 085ec6c..0000000 --- a/Storage/Storage/Internal_/Command/AVCommand.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net.Http; - -namespace LeanCloud.Storage.Internal { - public class AVCommand { - // 不同服务对应的服务器地址不同 - public virtual string Server => AVClient.CurrentConfiguration.ApiServer; - - public string Path { - get; set; - } - - public HttpMethod Method { - get; set; - } - - public Dictionary Headers { - get; set; - } - - public object Content { - get; set; - } - - internal Uri Uri { - get { - return new Uri($"{Server}/{AVClient.APIVersion}/{Path}"); - } - } - } -} diff --git a/Storage/Storage/Internal_/Command/AVCommandRunner.cs b/Storage/Storage/Internal_/Command/AVCommandRunner.cs deleted file mode 100644 index bbd6887..0000000 --- a/Storage/Storage/Internal_/Command/AVCommandRunner.cs +++ /dev/null @@ -1,180 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Threading; -using System.Threading.Tasks; -using System.Linq; -using Newtonsoft.Json; -using LeanCloud.Common; - -namespace LeanCloud.Storage.Internal { - /// - /// Command Runner. - /// - public class AVCommandRunner { - const string APPLICATION_JSON = "application/json"; - const string USE_PRODUCTION = "1"; - const string USE_DEVELOPMENT = "0"; - - private readonly HttpClient httpClient; - - public AVCommandRunner() { - httpClient = new HttpClient(); - ProductHeaderValue product = new ProductHeaderValue(AVClient.Name, AVClient.Version); - httpClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue(product)); - httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(APPLICATION_JSON)); - - var conf = AVClient.CurrentConfiguration; - // App ID - httpClient.DefaultRequestHeaders.Add("X-LC-Id", conf.ApplicationId); - // App Signature - long timestamp = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds; - if (!string.IsNullOrEmpty(conf.MasterKey) && AVClient.UseMasterKey) { - string sign = MD5.GetMd5String(timestamp + conf.MasterKey); - httpClient.DefaultRequestHeaders.Add("X-LC-Sign", $"{sign},{timestamp},master"); - } else { - string sign = MD5.GetMd5String(timestamp + conf.ApplicationKey); - httpClient.DefaultRequestHeaders.Add("X-LC-Sign", $"{sign},{timestamp}"); - } - // TODO Session - - // Production - httpClient.DefaultRequestHeaders.Add("X-LC-Prod", AVClient.UseProduction ? USE_PRODUCTION : USE_DEVELOPMENT); - } - - - /// - /// - /// - /// - /// - /// - public async Task> RunCommandAsync(AVCommand command,CancellationToken cancellationToken = default) { - string content = JsonConvert.SerializeObject(command.Content); - var request = new HttpRequestMessage { - RequestUri = command.Uri, - Method = command.Method, - Content = new StringContent(content) - }; - - request.Content.Headers.ContentType = new MediaTypeHeaderValue(APPLICATION_JSON); - // 特殊 Headers - if (command.Headers != null) { - foreach (KeyValuePair header in command.Headers) { - request.Headers.Add(header.Key, header.Value); - } - } - // Session Token - if (!request.Headers.Contains("X-LC-Session") && - AVUser.CurrentUser != null && - !string.IsNullOrEmpty(AVUser.CurrentUser.SessionToken)) { - request.Headers.Add("X-LC-Session", AVUser.CurrentUser.SessionToken); - } - HttpUtils.PrintRequest(httpClient, request, content); - - var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken); - request.Dispose(); - - var resultString = await response.Content.ReadAsStringAsync(); - response.Dispose(); - HttpUtils.PrintResponse(response, resultString); - - var ret = new Tuple(response.StatusCode, resultString); - - var responseCode = ret.Item1; - var contentString = ret.Item2; - - if (responseCode >= HttpStatusCode.InternalServerError) { - // Server error, return InternalServerError. - throw new AVException(AVException.ErrorCode.InternalServerError, contentString); - } - - if (responseCode < HttpStatusCode.OK || responseCode > HttpStatusCode.PartialContent) { - // 错误处理 - var data = JsonConvert.DeserializeObject>(contentString, new LeanCloudJsonConverter()); - if (data.TryGetValue("code", out object codeObj)) { - AVException.ErrorCode code = (AVException.ErrorCode)Enum.ToObject(typeof(AVException.ErrorCode), codeObj); - string detail = data["error"] as string; - throw new AVException(code, detail); - } else { - throw new AVException(AVException.ErrorCode.OtherCause, contentString); - } - } - - if (contentString != null) { - try { - var data = JsonConvert.DeserializeObject(contentString, new LeanCloudJsonConverter()); - return new Tuple(responseCode, (T)data); - } catch (Exception e) { - throw new AVException(AVException.ErrorCode.OtherCause, - "Invalid response from server", e); - } - } - - return new Tuple(responseCode, default); - } - - - // TODO (hallucinogen): move this out to a class to be used by Analytics - private const int MaximumBatchSize = 50; - - internal async Task>> ExecuteBatchRequests(IList requests, - CancellationToken cancellationToken) { - var results = new List>(); - int batchSize = requests.Count; - - IEnumerable remaining = requests; - while (batchSize > MaximumBatchSize) { - var process = remaining.Take(MaximumBatchSize).ToList(); - remaining = remaining.Skip(MaximumBatchSize); - - results.AddRange(await ExecuteBatchRequest(process, cancellationToken)); - - batchSize = remaining.Count(); - } - results.AddRange(await ExecuteBatchRequest(remaining.ToList(), cancellationToken)); - - return results; - } - - internal async Task>> ExecuteBatchRequest(IList requests, CancellationToken cancellationToken) { - var tasks = new List>>(); - int batchSize = requests.Count; - var tcss = new List>>(); - for (int i = 0; i < batchSize; ++i) { - var tcs = new TaskCompletionSource>(); - tcss.Add(tcs); - tasks.Add(tcs.Task); - } - - var encodedRequests = requests.Select(r => { - var results = new Dictionary { - { "method", r.Method.Method }, - { "path", $"/{AVClient.APIVersion}/{r.Path}" }, - }; - - if (r.Content != null) { - results["body"] = r.Content; - } - return results; - }).Cast().ToList(); - var command = new AVCommand { - Path = "batch", - Method = HttpMethod.Post, - Content = new Dictionary { - { "requests", encodedRequests } - } - }; - - try { - List> result = new List>(); - var response = await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command, cancellationToken); - return response.Item2.Cast>().ToList(); - } catch (Exception e) { - throw e; - } - } - } -} diff --git a/Storage/Storage/Internal_/Command/EngineCommand.cs b/Storage/Storage/Internal_/Command/EngineCommand.cs deleted file mode 100644 index 506ba27..0000000 --- a/Storage/Storage/Internal_/Command/EngineCommand.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System; - -namespace LeanCloud.Storage.Internal { - public class EngineCommand : AVCommand { - public override string Server => AVClient.CurrentConfiguration.EngineServer; - } -} diff --git a/Storage/Storage/Internal_/Command/RTMCommand.cs b/Storage/Storage/Internal_/Command/RTMCommand.cs deleted file mode 100644 index a73ffc8..0000000 --- a/Storage/Storage/Internal_/Command/RTMCommand.cs +++ /dev/null @@ -1,7 +0,0 @@ -using LeanCloud; - -namespace LeanCloud.Storage.Internal { - public class RTMCommand : AVCommand { - public override string Server => AVClient.CurrentConfiguration.RTMServer; - } -} diff --git a/Storage/Storage/Internal_/Encoding/AVDecoder.cs b/Storage/Storage/Internal_/Encoding/AVDecoder.cs deleted file mode 100644 index e18992e..0000000 --- a/Storage/Storage/Internal_/Encoding/AVDecoder.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using System.Linq; -using System.Collections.Generic; -using System.Globalization; -using LeanCloud.Utilities; - -namespace LeanCloud.Storage.Internal { - public class AVDecoder { - // This class isn't really a Singleton, but since it has no state, it's more efficient to get - // the default instance. - private static readonly AVDecoder instance = new AVDecoder(); - public static AVDecoder Instance { - get { - return instance; - } - } - - // Prevent default constructor. - private AVDecoder() { } - - public object Decode(object data) { - // 如果是字典类型 - if (data is IDictionary dict) { - if (dict.ContainsKey("__op")) { - return AVFieldOperations.Decode(dict); - } - if (dict.TryGetValue("__type", out object type)) { - string typeString = type as string; - switch (typeString) { - case "Date": - return ParseDate(dict["iso"] as string); - case "Bytes": - return Convert.FromBase64String(dict["base64"] as string); - case "Pointer": { - if (dict.Keys.Count > 3) { - return DecodeAVObject(dict); - } - return DecodePointer(dict["className"] as string, dict["objectId"] as string); - } - case "GeoPoint": - return new AVGeoPoint(Conversion.To(dict["latitude"]), - Conversion.To(dict["longitude"])); - case "Object": - return DecodeAVObject(dict); - case "Relation": - return AVRelationBase.CreateRelation(null, null, dict["className"] as string); - default: - break; - } - } - var converted = new Dictionary(); - foreach (var pair in dict) { - converted[pair.Key] = Decode(pair.Value); - } - return converted; - } - // 如果是数组类型 - if (data is IList list) { - return (from item in list - select Decode(item)).ToList(); - } - // 原样返回 - return data; - } - - protected virtual object DecodePointer(string className, string objectId) { - return AVObject.CreateWithoutData(className, objectId); - } - - protected virtual object DecodeAVObject(IDictionary dict) { - var className = dict["className"] as string; - var state = AVObjectCoder.Instance.Decode(dict, this); - return AVObject.FromState(state, className); - } - - public virtual IList DecodeList(object data) { - IList rtn = null; - var list = (IList)data; - if (list != null) { - rtn = new List(); - foreach (var item in list) { - rtn.Add((T)item); - } - } - return rtn; - } - - public static DateTime ParseDate(string input) { - return DateTime.ParseExact(input, - AVClient.DateFormatStrings, - CultureInfo.InvariantCulture, - DateTimeStyles.AssumeUniversal); - } - } -} diff --git a/Storage/Storage/Internal_/Encoding/AVEncoder.cs b/Storage/Storage/Internal_/Encoding/AVEncoder.cs deleted file mode 100644 index 2a104d8..0000000 --- a/Storage/Storage/Internal_/Encoding/AVEncoder.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using LeanCloud.Utilities; -using LeanCloud.Storage.Internal; - -namespace LeanCloud.Storage.Internal { - /// - /// A AVEncoder can be used to transform objects such as into JSON - /// data structures. - /// - /// - public abstract class AVEncoder { - public static bool IsValidType(object value) { - return value == null || - ReflectionHelpers.IsPrimitive(value.GetType()) || - value is string || - value is AVObject || - value is AVACL || - value is AVGeoPoint || - value is AVRelationBase || - value is DateTime || - value is byte[] || - Conversion.As>(value) != null || - Conversion.As>(value) != null; - } - - public object Encode(object value) { - // If this object has a special encoding, encode it and return the - // encoded object. Otherwise, just return the original object. - if (value is DateTime) { - return new Dictionary { - { "__type", "Date" }, - { "iso", ((DateTime)value).ToUniversalTime().ToString(AVClient.DateFormatStrings.First(), CultureInfo.InvariantCulture) } - }; - } - - if (value is byte[] bytes) { - return new Dictionary { - { "__type", "Bytes" }, - { "base64", Convert.ToBase64String(bytes) } - }; - } - - if (value is AVObject obj) { - return EncodeAVObject(obj); - } - - if (value is IJsonConvertible jsonConvertible) { - return jsonConvertible.ToJSON(); - } - - if (value is IDictionary) { - IDictionary dict = value as IDictionary; - var json = new Dictionary(); - foreach (var key in dict.Keys) { - object v = dict[key]; - json[key.ToString()] = Encode(v); - } - return json; - } - - if (value is IList) { - IList list = value as IList; - return EncodeList(list); - } - - if (value is IAVFieldOperation operation) { - return operation.Encode(); - } - - return value; - } - - protected abstract IDictionary EncodeAVObject(AVObject value); - - private object EncodeList(IList list) { - List newArray = new List(); - foreach (object item in list) { - newArray.Add(Encode(item)); - } - return newArray; - } - } -} diff --git a/Storage/Storage/Internal_/Encoding/AVObjectCoder.cs b/Storage/Storage/Internal_/Encoding/AVObjectCoder.cs deleted file mode 100644 index 1629084..0000000 --- a/Storage/Storage/Internal_/Encoding/AVObjectCoder.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace LeanCloud.Storage.Internal -{ - // TODO: (richardross) refactor entire LeanCloud coder interfaces. - public class AVObjectCoder - { - private static readonly AVObjectCoder instance = new AVObjectCoder(); - public static AVObjectCoder Instance - { - get - { - return instance; - } - } - - // Prevent default constructor. - private AVObjectCoder() { } - - public IDictionary Encode(T state, - IDictionary operations, - AVEncoder encoder) where T : IObjectState - { - var result = new Dictionary(); - foreach (var pair in operations) - { - // AVRPCSerialize the data - var operation = pair.Value; - - result[pair.Key] = encoder.Encode(operation); - } - - return result; - } - - public IObjectState Decode(IDictionary data, - AVDecoder decoder) - { - IDictionary serverData = new Dictionary(); - var mutableData = new Dictionary(data); - string objectId = ExtractFromDictionary(mutableData, "objectId", (obj) => - { - return obj as string; - }); - DateTime? createdAt = ExtractFromDictionary(mutableData, "createdAt", (obj) => - { - return (DateTime)obj; - }); - DateTime? updatedAt = ExtractFromDictionary(mutableData, "updatedAt", (obj) => - { - return (DateTime)obj; - }); - - AVACL acl = ExtractFromDictionary(mutableData, "ACL", (obj) => - { - return new AVACL(obj as IDictionary); - }); - - string className = ExtractFromDictionary(mutableData, "className", obj => - { - return obj as string; - }); - - if (createdAt != null && updatedAt == null) - { - updatedAt = createdAt; - } - - // Bring in the new server data. - foreach (var pair in mutableData) - { - if (pair.Key == "__type" || pair.Key == "className") - { - continue; - } - - var value = pair.Value; - serverData[pair.Key] = decoder.Decode(value); - } - - return new MutableObjectState - { - ObjectId = objectId, - ACL = acl, - CreatedAt = createdAt, - UpdatedAt = updatedAt, - ServerData = serverData, - ClassName = className - }; - } - - private T ExtractFromDictionary(IDictionary data, string key, Func action) - { - T result = default; - if (data.TryGetValue(key, out object val)) { - result = action(val); - data.Remove(key); - } - - return result; - } - } -} diff --git a/Storage/Storage/Internal_/Encoding/PointerOrLocalIdEncoder.cs b/Storage/Storage/Internal_/Encoding/PointerOrLocalIdEncoder.cs deleted file mode 100644 index ba50743..0000000 --- a/Storage/Storage/Internal_/Encoding/PointerOrLocalIdEncoder.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; - -namespace LeanCloud.Storage.Internal -{ - /// - /// A that encode as pointers. If the object - /// does not have an , uses a local id. - /// - public class PointerOrLocalIdEncoder : AVEncoder - { - // This class isn't really a Singleton, but since it has no state, it's more efficient to get - // the default instance. - private static readonly PointerOrLocalIdEncoder instance = new PointerOrLocalIdEncoder(); - public static PointerOrLocalIdEncoder Instance - { - get - { - return instance; - } - } - - protected override IDictionary EncodeAVObject(AVObject value) - { - if (value.ObjectId == null) - { - // TODO (hallucinogen): handle local id. For now we throw. - throw new ArgumentException("Cannot create a pointer to an object without an objectId"); - } - - return new Dictionary { - {"__type", "Pointer"}, - { "className", value.ClassName}, - { "objectId", value.ObjectId} - }; - } - - public IDictionary EncodeAVObject(AVObject value, bool isPointer) - { - if (isPointer) - { - return EncodeAVObject(value); - } - var operations = value.GetCurrentOperations(); - var operationJSON = AVObject.ToJSONObjectForSaving(operations); - var objectJSON = value.ToDictionary(kvp => kvp.Key, kvp => PointerOrLocalIdEncoder.Instance.Encode(kvp.Value)); - foreach (var kvp in operationJSON) { - objectJSON[kvp.Key] = kvp.Value; - } - if (value.CreatedAt.HasValue) { - objectJSON["createdAt"] = value.CreatedAt.Value.ToString(AVClient.DateFormatStrings.First(), CultureInfo.InvariantCulture); - } - if (value.UpdatedAt.HasValue) { - objectJSON["updatedAt"] = value.UpdatedAt.Value.ToString(AVClient.DateFormatStrings.First(), CultureInfo.InvariantCulture); - } - if(!string.IsNullOrEmpty(value.ObjectId)) { - objectJSON["objectId"] = value.ObjectId; - } - if (value.ACL != null) { - objectJSON["acl"] = Encode(value.ACL); - } - objectJSON["className"] = value.ClassName; - objectJSON["__type"] = "Object"; - return objectJSON; - } - } -} diff --git a/Storage/Storage/Internal_/File/Controller/AVFileController.cs b/Storage/Storage/Internal_/File/Controller/AVFileController.cs deleted file mode 100644 index 66657f7..0000000 --- a/Storage/Storage/Internal_/File/Controller/AVFileController.cs +++ /dev/null @@ -1,129 +0,0 @@ -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using System.Net; -using System.Net.Http; -using System.Collections.Generic; -using System.Linq; - -namespace LeanCloud.Storage.Internal { - public interface IFileUploader { - Task Upload(FileState state, Stream dataStream, IDictionary fileToken, IProgress progress, - CancellationToken cancellationToken); - } - - public class AVFileController { - const string QCloud = "qcloud"; - const string AWS = "s3"; - - //public async Task SaveAsync(FileState state, - // Stream dataStream, - // IProgress progress, - // CancellationToken cancellationToken = default) { - // if (state.Url != null) { - // return await SaveWithUrl(state); - // } - - // var data = await GetFileToken(state, cancellationToken); - // var fileToken = data.Item2; - // var provider = fileToken["provider"] as string; - // switch (provider) { - // case QCloud: - // return await new QCloudUploader().Upload(state, dataStream, fileToken, progress, cancellationToken); - // case AWS: - // return await new AWSUploader().Upload(state, dataStream, fileToken, progress, cancellationToken); - // default: - // return await new QiniuUploader().Upload(state, dataStream, fileToken, progress, cancellationToken); - // } - //} - - public async Task DeleteAsync(FileState state, CancellationToken cancellationToken) { - var command = new AVCommand { - Path = $"files/{state.ObjectId}", - Method = HttpMethod.Delete - }; - await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command, cancellationToken: cancellationToken); - } - - internal async Task SaveWithUrl(FileState state) { - Dictionary strs = new Dictionary { - { "url", state.Url.ToString() }, - { "name", state.Name }, - { "mime_type", state.MimeType }, - { "metaData", state.MetaData } - }; - AVCommand cmd = null; - - if (!string.IsNullOrEmpty(state.ObjectId)) { - cmd = new AVCommand { - Path = $"files/{state.ObjectId}", - Method = HttpMethod.Put, - Content = strs - }; - } else { - cmd = new AVCommand { - Path = "files", - Method = HttpMethod.Post, - Content = strs - }; - } - - var data = await AVPlugins.Instance.CommandRunner.RunCommandAsync>(cmd); - var result = data.Item2; - state.ObjectId = result["objectId"].ToString(); - return state; - } - - internal async Task>> GetFileToken(string name, IDictionary metaData, CancellationToken cancellationToken = default) { - IDictionary parameters = new Dictionary { - { "name", name }, - { "key", GetUniqueName(name) }, - { "__type", "File" }, - { "mime_type", AVFile.GetMIMEType(name) }, - { "metaData", metaData } - }; - - var command = new AVCommand { - Path = "fileTokens", - Method = HttpMethod.Post, - Content = parameters - }; - return await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command, cancellationToken); - } - - public async Task GetAsync(string objectId, CancellationToken cancellationToken) { - var command = new AVCommand { - Path = $"files/{objectId}", - Method = HttpMethod.Get - }; - var data = await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command, cancellationToken); - var jsonData = data.Item2; - cancellationToken.ThrowIfCancellationRequested(); - return new FileState { - ObjectId = jsonData["objectId"] as string, - Name = jsonData["name"] as string, - Url = new Uri(jsonData["url"] as string, UriKind.Absolute), - }; - } - - internal static string GetUniqueName(string name) { - string key = Guid.NewGuid().ToString(); - string extension = Path.GetExtension(name); - key += extension; - return key; - } - - internal static string Random(int length) { - const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"; - var random = new Random(); - return new string(Enumerable.Repeat(chars, length) - .Select(s => s[random.Next(s.Length)]).ToArray()); - } - - internal static double CalcProgress(double already, double total) { - var pv = (1.0 * already / total); - return Math.Round(pv, 3); - } - } -} diff --git a/Storage/Storage/Internal_/File/Controller/AWSUploader.cs b/Storage/Storage/Internal_/File/Controller/AWSUploader.cs deleted file mode 100644 index a134041..0000000 --- a/Storage/Storage/Internal_/File/Controller/AWSUploader.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Threading.Tasks; -using System.Threading; -using System.IO; -using System.Collections.Generic; -using System.Net.Http; -using System.Net.Http.Headers; - -namespace LeanCloud.Storage.Internal { - internal class AWSUploader { - internal string UploadUrl { - get; set; - } - - internal string MimeType { - get; set; - } - - internal Stream Stream { - get; set; - } - - internal async Task Upload(CancellationToken cancellationToken = default) { - HttpClient client = new HttpClient(); - HttpRequestMessage request = new HttpRequestMessage { - RequestUri = new Uri(UploadUrl), - Method = HttpMethod.Put, - Content = new StreamContent(Stream) - }; - request.Headers.Add("Cache-Control", "public, max-age=31536000"); - request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(MimeType); - request.Content.Headers.ContentLength = Stream.Length; - HttpResponseMessage response = await client.SendAsync(request, cancellationToken); - response.Dispose(); - client.Dispose(); - request.Dispose(); - } - } -} diff --git a/Storage/Storage/Internal_/File/Controller/QCloudUploader.cs b/Storage/Storage/Internal_/File/Controller/QCloudUploader.cs deleted file mode 100644 index bfea7aa..0000000 --- a/Storage/Storage/Internal_/File/Controller/QCloudUploader.cs +++ /dev/null @@ -1,216 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using LeanCloud.Common; - -namespace LeanCloud.Storage.Internal { - internal class QCloudUploader { - private object mutex = new object(); - - bool done; - private long sliceSize = (long)CommonSize.KB512; - - internal string FileName { - get; set; - } - - internal string UploadUrl { - get; set; - } - - internal string Token { - get; set; - } - - internal string Bucket { - get; set; - } - - internal Stream Stream { - get; set; - } - - internal async Task Upload(CancellationToken cancellationToken = default) { - var result = await FileSlice(cancellationToken); - if (done) { - return; - } - var response = result; - var resumeData = response["data"] as IDictionary; - if (resumeData.ContainsKey("access_url")) { - return; - } - var sliceSession = resumeData["session"].ToString(); - var sliceOffset = long.Parse(resumeData["offset"].ToString()); - await UploadSlice(sliceSession, sliceOffset, Stream, null, cancellationToken); - } - - async Task UploadSlice( - string sessionId, - long offset, - Stream dataStream, - IProgress progress, - CancellationToken cancellationToken) { - - long dataLength = dataStream.Length; - if (progress != null) { - lock (mutex) { - progress.Report(new AVUploadProgressEventArgs() { - Progress = AVFileController.CalcProgress(offset, dataLength) - }); - } - } - - if (offset == dataLength) { - return; - } - - var sliceFile = GetNextBinary(offset, dataStream); - var result = await ExcuteUpload(sessionId, offset, sliceFile, cancellationToken); - offset += sliceFile.Length; - if (offset == dataLength) { - done = true; - return; - } - var response = result; - var resumeData = response["data"] as IDictionary; - var sliceSession = resumeData["session"].ToString(); - await UploadSlice(sliceSession, offset, dataStream, progress, cancellationToken); - } - - Task> ExcuteUpload(string sessionId, long offset, byte[] sliceFile, CancellationToken cancellationToken) { - var body = new Dictionary(); - body.Add("op", "upload_slice"); - body.Add("session", sessionId); - body.Add("offset", offset.ToString()); - - return PostToQCloud(body, sliceFile, cancellationToken); - } - - async Task> FileSlice(CancellationToken cancellationToken) { - SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider(); - var body = new Dictionary(); - if (Stream.Length <= (long)CommonSize.KB512) { - body.Add("op", "upload"); - body.Add("sha", HexStringFromBytes(sha1.ComputeHash(Stream))); - var wholeFile = GetNextBinary(0, Stream); - var result = await PostToQCloud(body, wholeFile, cancellationToken); - return result; - } else { - body.Add("op", "upload_slice"); - body.Add("filesize", Stream.Length); - body.Add("sha", HexStringFromBytes(sha1.ComputeHash(Stream))); - body.Add("slice_size", (long)CommonSize.KB512); - } - - return await PostToQCloud(body, null, cancellationToken); - } - public static string HexStringFromBytes(byte[] bytes) { - var sb = new StringBuilder(); - foreach (byte b in bytes) { - var hex = b.ToString("x2"); - sb.Append(hex); - } - return sb.ToString(); - } - - public static string SHA1HashStringForUTF8String(string s) { - byte[] bytes = Encoding.UTF8.GetBytes(s); - - SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider(); - byte[] hashBytes = sha1.ComputeHash(bytes); - - return HexStringFromBytes(hashBytes); - } - - async Task> PostToQCloud( - Dictionary body, - byte[] sliceFile, - CancellationToken cancellationToken) { - - string contentType; - long contentLength; - - var tempStream = HttpUploadFile(sliceFile, FileName, out contentType, out contentLength, body); - - var client = new HttpClient(); - var request = new HttpRequestMessage { - RequestUri = new Uri(UploadUrl), - Method = HttpMethod.Post, - Content = new StreamContent(tempStream) - }; - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Authorization", Token); - request.Content.Headers.Add("Content-Type", contentType); - - var response = await client.SendAsync(request); - client.Dispose(); - request.Dispose(); - var content = await response.Content.ReadAsStringAsync(); - response.Dispose(); - // 修改反序列化返回 - return await JsonUtils.DeserializeObjectAsync>(content, new LeanCloudJsonConverter()); - } - public static Stream HttpUploadFile(byte[] file, string fileName, out string contentType, out long contentLength, IDictionary nvc) { - string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x"); - byte[] boundarybytes = StringToAscii("\r\n--" + boundary + "\r\n"); - contentType = "multipart/form-data; boundary=" + boundary; - - MemoryStream rs = new MemoryStream(); - - string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}"; - foreach (string key in nvc.Keys) { - rs.Write(boundarybytes, 0, boundarybytes.Length); - string formitem = string.Format(formdataTemplate, key, nvc[key]); - byte[] formitembytes = System.Text.Encoding.UTF8.GetBytes(formitem); - rs.Write(formitembytes, 0, formitembytes.Length); - } - rs.Write(boundarybytes, 0, boundarybytes.Length); - - if (file != null) { - string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n"; - string header = string.Format(headerTemplate, "fileContent", fileName, "application/octet-stream"); - byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes(header); - rs.Write(headerbytes, 0, headerbytes.Length); - - rs.Write(file, 0, file.Length); - } - - byte[] trailer = StringToAscii("\r\n--" + boundary + "--\r\n"); - rs.Write(trailer, 0, trailer.Length); - contentLength = rs.Length; - - rs.Position = 0; - var tempBuffer = new byte[rs.Length]; - rs.Read(tempBuffer, 0, tempBuffer.Length); - - return new MemoryStream(tempBuffer); - } - - public static byte[] StringToAscii(string s) { - byte[] retval = new byte[s.Length]; - for (int ix = 0; ix < s.Length; ++ix) { - char ch = s[ix]; - if (ch <= 0x7f) retval[ix] = (byte)ch; - else retval[ix] = (byte)'?'; - } - return retval; - } - - byte[] GetNextBinary(long completed, Stream dataStream) { - if (completed + sliceSize > dataStream.Length) { - sliceSize = dataStream.Length - completed; - } - - byte[] chunkBinary = new byte[sliceSize]; - dataStream.Seek(completed, SeekOrigin.Begin); - dataStream.Read(chunkBinary, 0, (int)sliceSize); - return chunkBinary; - } - } -} diff --git a/Storage/Storage/Internal_/File/Controller/QiniuUploader.cs b/Storage/Storage/Internal_/File/Controller/QiniuUploader.cs deleted file mode 100644 index aa2f8bb..0000000 --- a/Storage/Storage/Internal_/File/Controller/QiniuUploader.cs +++ /dev/null @@ -1,294 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Net.Http; -using System.Net.Http.Headers; -using LeanCloud.Common; - -namespace LeanCloud.Storage.Internal { - internal enum CommonSize : long { - MB4 = 1024 * 1024 * 4, - MB1 = 1024 * 1024, - KB512 = 1024 * 1024 / 2, - KB256 = 1024 * 1024 / 4 - } - - internal class QiniuUploader { - private static readonly int BLOCKSIZE = 1024 * 1024 * 4; - internal static string UP_HOST = "https://up.qbox.me"; - private readonly object mutex = new object(); - - public int counter; - public Stream frozenData; - public string bucketId; - public string bucket; - public string token; - public long completed; - public List block_ctxes = new List(); - - internal string Key { - get; set; - } - - internal string Token { - get; set; - } - - internal string MimeType { - get; set; - } - - internal IDictionary MetaData { - get; set; - } - - internal Stream Stream { - get; set; - } - - internal async Task Upload(CancellationToken cancellationToken = default) { - await UploadNextChunk(string.Empty, 0, null); - } - - async Task UploadNextChunk(string context, long offset, IProgress progress) { - var totalSize = Stream.Length; - var remainingSize = totalSize - completed; - - if (progress != null) { - lock (mutex) { - progress.Report(new AVUploadProgressEventArgs() { - Progress = AVFileController.CalcProgress(completed, totalSize) - }); - } - } - if (completed == totalSize) { - await QiniuMakeFile(totalSize, block_ctxes.ToArray(), CancellationToken.None); - } else if (completed % BLOCKSIZE == 0) { - var firstChunkBinary = GetChunkBinary(); - - var blockSize = remainingSize > BLOCKSIZE ? BLOCKSIZE : remainingSize; - var result = await MakeBlock(firstChunkBinary, blockSize); - var dict = result; - var ctx = dict["ctx"].ToString(); - offset = long.Parse(dict["offset"].ToString()); - var host = dict["host"].ToString(); - - completed += firstChunkBinary.Length; - if (completed % BLOCKSIZE == 0 || completed == totalSize) { - block_ctxes.Add(ctx); - } - - await UploadNextChunk(ctx, offset, progress); - } else { - var chunkBinary = GetChunkBinary(); - var result = await PutChunk(chunkBinary, context, offset); - var dict = result; - var ctx = dict["ctx"].ToString(); - - offset = long.Parse(dict["offset"].ToString()); - var host = dict["host"].ToString(); - completed += chunkBinary.Length; - if (completed % BLOCKSIZE == 0 || completed == totalSize) { - block_ctxes.Add(ctx); - } - await UploadNextChunk(ctx, offset, progress); - } - } - - byte[] GetChunkBinary() { - long chunkSize = (long)CommonSize.MB1; - if (completed + chunkSize > Stream.Length) { - chunkSize = Stream.Length - completed; - } - byte[] chunkBinary = new byte[chunkSize]; - Stream.Seek(completed, SeekOrigin.Begin); - Stream.Read(chunkBinary, 0, (int)chunkSize); - return chunkBinary; - } - - IList> GetQiniuRequestHeaders() { - IList> makeBlockHeaders = new List>(); - string authHead = "UpToken " + Token; - makeBlockHeaders.Add(new KeyValuePair("Authorization", authHead)); - return makeBlockHeaders; - } - - async Task> MakeBlock(byte[] firstChunkBinary, long blcokSize = 4194304) { - MemoryStream firstChunkData = new MemoryStream(firstChunkBinary, 0, firstChunkBinary.Length); - - var client = new HttpClient(); - var request = new HttpRequestMessage { - RequestUri = new Uri($"{UP_HOST}/mkblk/{blcokSize}"), - Method = HttpMethod.Post, - Content = new StreamContent(firstChunkData) - }; - var headers = GetQiniuRequestHeaders(); - foreach (var header in headers) { - request.Headers.Add(header.Key, header.Value); - } - - request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); - var response = await client.SendAsync(request); - client.Dispose(); - request.Dispose(); - var content = await response.Content.ReadAsStringAsync(); - response.Dispose(); - return await JsonUtils.DeserializeObjectAsync>(content, new LeanCloudJsonConverter()); - } - - async Task> PutChunk(byte[] chunkBinary, string LastChunkctx, long currentChunkOffsetInBlock) { - MemoryStream chunkData = new MemoryStream(chunkBinary, 0, chunkBinary.Length); - var client = new HttpClient(); - var request = new HttpRequestMessage { - RequestUri = new Uri($"{UP_HOST}/bput/{LastChunkctx}/{currentChunkOffsetInBlock}"), - Method = HttpMethod.Post, - Content = new StreamContent(chunkData) - }; - var headers = GetQiniuRequestHeaders(); - foreach (var header in headers) { - request.Headers.Add(header.Key, header.Value); - } - var response = await client.SendAsync(request); - client.Dispose(); - request.Dispose(); - var content = await response.Content.ReadAsStringAsync(); - response.Dispose(); - return await JsonUtils.DeserializeObjectAsync>(content); - } - - internal async Task> QiniuMakeFile(long fsize, string[] ctxes, CancellationToken cancellationToken) { - StringBuilder urlBuilder = new StringBuilder(); - urlBuilder.AppendFormat("{0}/mkfile/{1}", UP_HOST, fsize); - if (!string.IsNullOrEmpty(Key)) { - urlBuilder.AppendFormat("/key/{0}", ToBase64URLSafe(Key)); - } - var metaData = GetMetaData(); - - StringBuilder sb = new StringBuilder(); - foreach (string _key in metaData.Keys) { - sb.AppendFormat("/{0}/{1}", _key, ToBase64URLSafe(metaData[_key].ToString())); - } - urlBuilder.Append(sb.ToString()); - - int proCount = ctxes.Length; - Stream body = new MemoryStream(); - - for (int i = 0; i < proCount; i++) { - byte[] bctx = StringToAscii(ctxes[i]); - body.Write(bctx, 0, bctx.Length); - if (i != proCount - 1) { - body.WriteByte((byte)','); - } - } - body.Seek(0, SeekOrigin.Begin); - - var client = new HttpClient(); - var request = new HttpRequestMessage { - RequestUri = new Uri(urlBuilder.ToString()), - Method = HttpMethod.Post, - Content = new StreamContent(body) - }; - request.Headers.Add("Authorization", $"UpToken {Token}"); - request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("text/plain"); - - var response = await client.SendAsync(request); - client.Dispose(); - request.Dispose(); - var content = await response.Content.ReadAsStringAsync(); - response.Dispose(); - return await JsonUtils.DeserializeObjectAsync>(content); - } - - internal void MergeFromJSON(FileState state, IDictionary jsonData) { - lock (this.mutex) { - string url = jsonData["url"] as string; - state.Url = new Uri(url, UriKind.Absolute); - state.bucketId = FetchBucketId(url); - state.token = jsonData["token"] as string; - state.bucket = jsonData["bucket"] as string; - state.ObjectId = jsonData["objectId"] as string; - } - } - - string FetchBucketId(string url) { - var elements = url.Split('/'); - - return elements[elements.Length - 1]; - } - - public static byte[] StringToAscii(string s) { - byte[] retval = new byte[s.Length]; - for (int ix = 0; ix < s.Length; ++ix) { - char ch = s[ix]; - if (ch <= 0x7f) - retval[ix] = (byte)ch; - else - retval[ix] = (byte)'?'; - } - return retval; - } - - public static string ToBase64URLSafe(string str) { - return Encode(str); - } - - public static string Encode(byte[] bs) { - if (bs == null || bs.Length == 0) - return ""; - string encodedStr = Convert.ToBase64String(bs); - encodedStr = encodedStr.Replace('+', '-').Replace('/', '_'); - return encodedStr; - } - - public static string Encode(string text) { - if (string.IsNullOrEmpty(text)) - return ""; - byte[] bs = Encoding.UTF8.GetBytes(text); - string encodedStr = Convert.ToBase64String(bs); - encodedStr = encodedStr.Replace('+', '-').Replace('/', '_'); - return encodedStr; - } - - internal static string GetMD5Code(Stream data) { - MD5 md5 = new MD5CryptoServiceProvider(); - byte[] retVal = md5.ComputeHash(data); - - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < retVal.Length; i++) { - sb.Append(retVal[i].ToString("x2")); - } - return sb.ToString(); - - } - - internal IDictionary GetMetaData() { - IDictionary rtn = new Dictionary(); - - if (MetaData != null) { - foreach (var meta in MetaData) { - rtn.Add(meta.Key, meta.Value); - } - } - MergeDic(rtn, "mime_type", AVFile.GetMIMEType(MimeType)); - MergeDic(rtn, "size", Stream.Length); - MergeDic(rtn, "_checksum", GetMD5Code(Stream)); - if (AVUser.CurrentUser != null) - if (AVUser.CurrentUser.ObjectId != null) - MergeDic(rtn, "owner", AVUser.CurrentUser.ObjectId); - - return rtn; - } - - internal void MergeDic(IDictionary dic, string key, object value) { - if (dic.ContainsKey(key)) { - dic[key] = value; - } else { - dic.Add(key, value); - } - } - } -} diff --git a/Storage/Storage/Internal_/File/Cryptography/MD5/MD5.cs b/Storage/Storage/Internal_/File/Cryptography/MD5/MD5.cs deleted file mode 100644 index 53efa04..0000000 --- a/Storage/Storage/Internal_/File/Cryptography/MD5/MD5.cs +++ /dev/null @@ -1,566 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.IO; - -namespace LeanCloud.Storage.Internal -{ - /// - /// 弥补Windows Phone 8 API没有自带MD5加密的拓展方法。 - /// - internal class MD5CryptoServiceProvider : MD5 - { - public MD5CryptoServiceProvider() - : base() - { - } - } - /// - /// Summary description for MD5. - /// - internal class MD5 : IDisposable - { - static public MD5 Create(string hashName) - { - if (hashName == "MD5") - return new MD5(); - else - throw new NotSupportedException(); - } - - static public string GetMd5String(String source) - { - MD5 md = MD5CryptoServiceProvider.Create(); - byte[] hash; - - //Create a new instance of ASCIIEncoding to - //convert the string into an array of Unicode bytes. - UTF8Encoding enc = new UTF8Encoding(); - // ASCIIEncoding enc = new ASCIIEncoding(); - - //Convert the string into an array of bytes. - byte[] buffer = enc.GetBytes(source); - - //Create the hash value from the array of bytes. - hash = md.ComputeHash(buffer); - - StringBuilder sb = new StringBuilder(); - foreach (byte b in hash) - sb.Append(b.ToString("x2")); - return sb.ToString(); - } - - static public MD5 Create() - { - return new MD5(); - } - - #region base implementation of the MD5 - #region constants - private const byte S11 = 7; - private const byte S12 = 12; - private const byte S13 = 17; - private const byte S14 = 22; - private const byte S21 = 5; - private const byte S22 = 9; - private const byte S23 = 14; - private const byte S24 = 20; - private const byte S31 = 4; - private const byte S32 = 11; - private const byte S33 = 16; - private const byte S34 = 23; - private const byte S41 = 6; - private const byte S42 = 10; - private const byte S43 = 15; - private const byte S44 = 21; - private static readonly byte[] PADDING = { - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - #endregion - - #region F, G, H and I are basic MD5 functions. - static private uint F(uint x, uint y, uint z) - { - return (((x) & (y)) | ((~x) & (z))); - } - static private uint G(uint x, uint y, uint z) - { - return (((x) & (z)) | ((y) & (~z))); - } - static private uint H(uint x, uint y, uint z) - { - return ((x) ^ (y) ^ (z)); - } - static private uint I(uint x, uint y, uint z) - { - return ((y) ^ ((x) | (~z))); - } - #endregion - - #region rotates x left n bits. - /// - /// rotates x left n bits. - /// - /// - /// - /// - static private uint ROTATE_LEFT(uint x, byte n) - { - return (((x) << (n)) | ((x) >> (32 - (n)))); - } - #endregion - - #region FF, GG, HH, and II transformations - /// FF, GG, HH, and II transformations - /// for rounds 1, 2, 3, and 4. - /// Rotation is separate from addition to prevent recomputation. - static private void FF(ref uint a, uint b, uint c, uint d, uint x, byte s, uint ac) - { - (a) += F((b), (c), (d)) + (x) + (uint)(ac); - (a) = ROTATE_LEFT((a), (s)); - (a) += (b); - } - static private void GG(ref uint a, uint b, uint c, uint d, uint x, byte s, uint ac) - { - (a) += G((b), (c), (d)) + (x) + (uint)(ac); - (a) = ROTATE_LEFT((a), (s)); - (a) += (b); - } - static private void HH(ref uint a, uint b, uint c, uint d, uint x, byte s, uint ac) - { - (a) += H((b), (c), (d)) + (x) + (uint)(ac); - (a) = ROTATE_LEFT((a), (s)); - (a) += (b); - } - static private void II(ref uint a, uint b, uint c, uint d, uint x, byte s, uint ac) - { - (a) += I((b), (c), (d)) + (x) + (uint)(ac); - (a) = ROTATE_LEFT((a), (s)); - (a) += (b); - } - #endregion - - #region context info - /// - /// state (ABCD) - /// - uint[] state = new uint[4]; - - /// - /// number of bits, modulo 2^64 (lsb first) - /// - uint[] count = new uint[2]; - - /// - /// input buffer - /// - byte[] buffer = new byte[64]; - #endregion - - internal MD5() - { - Initialize(); - } - - /// - /// MD5 initialization. Begins an MD5 operation, writing a new context. - /// - /// - /// The RFC named it "MD5Init" - /// - public virtual void Initialize() - { - count[0] = count[1] = 0; - - // Load magic initialization constants. - state[0] = 0x67452301; - state[1] = 0xefcdab89; - state[2] = 0x98badcfe; - state[3] = 0x10325476; - } - - /// - /// MD5 block update operation. Continues an MD5 message-digest - /// operation, processing another message block, and updating the - /// context. - /// - /// - /// - /// - /// The RFC Named it MD5Update - protected virtual void HashCore(byte[] input, int offset, int count) - { - int i; - int index; - int partLen; - - // Compute number of bytes mod 64 - index = (int)((this.count[0] >> 3) & 0x3F); - - // Update number of bits - if ((this.count[0] += (uint)((uint)count << 3)) < ((uint)count << 3)) - this.count[1]++; - this.count[1] += ((uint)count >> 29); - - partLen = 64 - index; - - // Transform as many times as possible. - if (count >= partLen) - { - Buffer.BlockCopy(input, offset, this.buffer, index, partLen); - Transform(this.buffer, 0); - - for (i = partLen; i + 63 < count; i += 64) - Transform(input, offset + i); - - index = 0; - } - else - i = 0; - - // Buffer remaining input - Buffer.BlockCopy(input, offset + i, this.buffer, index, count - i); - } - - /// - /// MD5 finalization. Ends an MD5 message-digest operation, writing the - /// the message digest and zeroizing the context. - /// - /// message digest - /// The RFC named it MD5Final - protected virtual byte[] HashFinal() - { - byte[] digest = new byte[16]; - byte[] bits = new byte[8]; - int index, padLen; - - // Save number of bits - Encode(bits, 0, this.count, 0, 8); - - // Pad out to 56 mod 64. - index = (int)((uint)(this.count[0] >> 3) & 0x3f); - padLen = (index < 56) ? (56 - index) : (120 - index); - HashCore(PADDING, 0, padLen); - - // Append length (before padding) - HashCore(bits, 0, 8); - - // Store state in digest - Encode(digest, 0, state, 0, 16); - - // Zeroize sensitive information. - count[0] = count[1] = 0; - state[0] = 0; - state[1] = 0; - state[2] = 0; - state[3] = 0; - - // initialize again, to be ready to use - Initialize(); - - return digest; - } - - /// - /// MD5 basic transformation. Transforms state based on 64 bytes block. - /// - /// - /// - private void Transform(byte[] block, int offset) - { - uint a = state[0], b = state[1], c = state[2], d = state[3]; - uint[] x = new uint[16]; - Decode(x, 0, block, offset, 64); - - // Round 1 - FF(ref a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */ - FF(ref d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */ - FF(ref c, d, a, b, x[2], S13, 0x242070db); /* 3 */ - FF(ref b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */ - FF(ref a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */ - FF(ref d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */ - FF(ref c, d, a, b, x[6], S13, 0xa8304613); /* 7 */ - FF(ref b, c, d, a, x[7], S14, 0xfd469501); /* 8 */ - FF(ref a, b, c, d, x[8], S11, 0x698098d8); /* 9 */ - FF(ref d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */ - FF(ref c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ - FF(ref b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ - FF(ref a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ - FF(ref d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ - FF(ref c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ - FF(ref b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ - - // Round 2 - GG(ref a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */ - GG(ref d, a, b, c, x[6], S22, 0xc040b340); /* 18 */ - GG(ref c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ - GG(ref b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */ - GG(ref a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */ - GG(ref d, a, b, c, x[10], S22, 0x2441453); /* 22 */ - GG(ref c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ - GG(ref b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */ - GG(ref a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */ - GG(ref d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ - GG(ref c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */ - GG(ref b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */ - GG(ref a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ - GG(ref d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */ - GG(ref c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */ - GG(ref b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ - - // Round 3 - HH(ref a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */ - HH(ref d, a, b, c, x[8], S32, 0x8771f681); /* 34 */ - HH(ref c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ - HH(ref b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ - HH(ref a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */ - HH(ref d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */ - HH(ref c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */ - HH(ref b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ - HH(ref a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ - HH(ref d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */ - HH(ref c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */ - HH(ref b, c, d, a, x[6], S34, 0x4881d05); /* 44 */ - HH(ref a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */ - HH(ref d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ - HH(ref c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ - HH(ref b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */ - - // Round 4 - II(ref a, b, c, d, x[0], S41, 0xf4292244); /* 49 */ - II(ref d, a, b, c, x[7], S42, 0x432aff97); /* 50 */ - II(ref c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ - II(ref b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */ - II(ref a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ - II(ref d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */ - II(ref c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ - II(ref b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */ - II(ref a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */ - II(ref d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ - II(ref c, d, a, b, x[6], S43, 0xa3014314); /* 59 */ - II(ref b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ - II(ref a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */ - II(ref d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ - II(ref c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */ - II(ref b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */ - - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - - // Zeroize sensitive information. - for (int i = 0; i < x.Length; i++) - x[i] = 0; - } - - /// - /// Encodes input (uint) into output (byte). Assumes len is - /// multiple of 4. - /// - /// - /// - /// - /// - /// - private static void Encode(byte[] output, int outputOffset, uint[] input, int inputOffset, int count) - { - int i, j; - int end = outputOffset + count; - for (i = inputOffset, j = outputOffset; j < end; i++, j += 4) - { - output[j] = (byte)(input[i] & 0xff); - output[j + 1] = (byte)((input[i] >> 8) & 0xff); - output[j + 2] = (byte)((input[i] >> 16) & 0xff); - output[j + 3] = (byte)((input[i] >> 24) & 0xff); - } - } - - /// - /// Decodes input (byte) into output (uint). Assumes len is - /// a multiple of 4. - /// - /// - /// - /// - /// - /// - static private void Decode(uint[] output, int outputOffset, byte[] input, int inputOffset, int count) - { - int i, j; - int end = inputOffset + count; - for (i = outputOffset, j = inputOffset; j < end; i++, j += 4) - output[i] = ((uint)input[j]) | (((uint)input[j + 1]) << 8) | (((uint)input[j + 2]) << 16) | (((uint)input[j + 3]) << 24); - } - #endregion - - #region expose the same interface as the regular MD5 object - - protected byte[] HashValue; - protected int State; - public virtual bool CanReuseTransform - { - get - { - return true; - } - } - - public virtual bool CanTransformMultipleBlocks - { - get - { - return true; - } - } - public virtual byte[] Hash - { - get - { - if (this.State != 0) - throw new InvalidOperationException(); - return (byte[])HashValue.Clone(); - } - } - public virtual int HashSize - { - get - { - return HashSizeValue; - } - } - protected int HashSizeValue = 128; - - public virtual int InputBlockSize - { - get - { - return 1; - } - } - public virtual int OutputBlockSize - { - get - { - return 1; - } - } - - public void Clear() - { - Dispose(true); - } - - public byte[] ComputeHash(byte[] buffer) - { - return ComputeHash(buffer, 0, buffer.Length); - } - public byte[] ComputeHash(byte[] buffer, int offset, int count) - { - Initialize(); - HashCore(buffer, offset, count); - HashValue = HashFinal(); - return (byte[])HashValue.Clone(); - } - - public byte[] ComputeHash(Stream inputStream) - { - Initialize(); - int count; - byte[] buffer = new byte[4096]; - while (0 < (count = inputStream.Read(buffer, 0, 4096))) - { - HashCore(buffer, 0, count); - } - HashValue = HashFinal(); - return (byte[])HashValue.Clone(); - } - - public int TransformBlock( - byte[] inputBuffer, - int inputOffset, - int inputCount, - byte[] outputBuffer, - int outputOffset - ) - { - if (inputBuffer == null) - { - throw new ArgumentNullException("inputBuffer"); - } - if (inputOffset < 0) - { - throw new ArgumentOutOfRangeException("inputOffset"); - } - if ((inputCount < 0) || (inputCount > inputBuffer.Length)) - { - throw new ArgumentException("inputCount"); - } - if ((inputBuffer.Length - inputCount) < inputOffset) - { - throw new ArgumentOutOfRangeException("inputOffset"); - } - if (this.State == 0) - { - Initialize(); - this.State = 1; - } - - HashCore(inputBuffer, inputOffset, inputCount); - if ((inputBuffer != outputBuffer) || (inputOffset != outputOffset)) - { - Buffer.BlockCopy(inputBuffer, inputOffset, outputBuffer, outputOffset, inputCount); - } - return inputCount; - } - public byte[] TransformFinalBlock( - byte[] inputBuffer, - int inputOffset, - int inputCount - ) - { - if (inputBuffer == null) - { - throw new ArgumentNullException("inputBuffer"); - } - if (inputOffset < 0) - { - throw new ArgumentOutOfRangeException("inputOffset"); - } - if ((inputCount < 0) || (inputCount > inputBuffer.Length)) - { - throw new ArgumentException("inputCount"); - } - if ((inputBuffer.Length - inputCount) < inputOffset) - { - throw new ArgumentOutOfRangeException("inputOffset"); - } - if (this.State == 0) - { - Initialize(); - } - HashCore(inputBuffer, inputOffset, inputCount); - HashValue = HashFinal(); - byte[] buffer = new byte[inputCount]; - Buffer.BlockCopy(inputBuffer, inputOffset, buffer, 0, inputCount); - this.State = 0; - return buffer; - } - #endregion - - protected virtual void Dispose(bool disposing) - { - if (!disposing) - Initialize(); - } - public void Dispose() - { - Dispose(true); - } - } -} - diff --git a/Storage/Storage/Internal_/File/Cryptography/SHA1/SHA1CryptoServiceProvider.cs b/Storage/Storage/Internal_/File/Cryptography/SHA1/SHA1CryptoServiceProvider.cs deleted file mode 100644 index 74f6e09..0000000 --- a/Storage/Storage/Internal_/File/Cryptography/SHA1/SHA1CryptoServiceProvider.cs +++ /dev/null @@ -1,495 +0,0 @@ -// -// System.Security.Cryptography.SHA1CryptoServiceProvider.cs -// -// Authors: -// Matthew S. Ford (Matthew.S.Ford@Rose-Hulman.Edu) -// Sebastien Pouliot (sebastien@ximian.com) -// -// Copyright 2001 by Matthew S. Ford. -// Copyright (C) 2004, 2005, 2008 Novell, Inc (http://www.novell.com) -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -// Note: -// The MS Framework includes two (almost) identical class for SHA1. -// SHA1Managed is a 100% managed implementation. -// SHA1CryptoServiceProvider (this file) is a wrapper on CryptoAPI. -// Mono must provide those two class for binary compatibility. -// In our case both class are wrappers around a managed internal class SHA1Internal. - -using System.IO; -using System; -using System.Runtime.InteropServices; - -namespace LeanCloud.Storage.Internal -{ - - internal class SHA1Internal - { - - private const int BLOCK_SIZE_BYTES = 64; - private uint[] _H; // these are my chaining variables - private ulong count; - private byte[] _ProcessingBuffer; // Used to start data when passed less than a block worth. - private int _ProcessingBufferCount; // Counts how much data we have stored that still needs processed. - private uint[] buff; - - public SHA1Internal() - { - _H = new uint[5]; - _ProcessingBuffer = new byte[BLOCK_SIZE_BYTES]; - buff = new uint[80]; - - Initialize(); - } - - public void HashCore(byte[] rgb, int ibStart, int cbSize) - { - int i; - - if (_ProcessingBufferCount != 0) - { - if (cbSize < (BLOCK_SIZE_BYTES - _ProcessingBufferCount)) - { - System.Buffer.BlockCopy(rgb, ibStart, _ProcessingBuffer, _ProcessingBufferCount, cbSize); - _ProcessingBufferCount += cbSize; - return; - } - else - { - i = (BLOCK_SIZE_BYTES - _ProcessingBufferCount); - System.Buffer.BlockCopy(rgb, ibStart, _ProcessingBuffer, _ProcessingBufferCount, i); - ProcessBlock(_ProcessingBuffer, 0); - _ProcessingBufferCount = 0; - ibStart += i; - cbSize -= i; - } - } - - for (i = 0; i < cbSize - cbSize % BLOCK_SIZE_BYTES; i += BLOCK_SIZE_BYTES) - { - ProcessBlock(rgb, (uint)(ibStart + i)); - } - - if (cbSize % BLOCK_SIZE_BYTES != 0) - { - System.Buffer.BlockCopy(rgb, cbSize - cbSize % BLOCK_SIZE_BYTES + ibStart, _ProcessingBuffer, 0, cbSize % BLOCK_SIZE_BYTES); - _ProcessingBufferCount = cbSize % BLOCK_SIZE_BYTES; - } - } - - public byte[] HashFinal() - { - byte[] hash = new byte[20]; - - ProcessFinalBlock(_ProcessingBuffer, 0, _ProcessingBufferCount); - - for (int i = 0; i < 5; i++) - { - for (int j = 0; j < 4; j++) - { - hash[i * 4 + j] = (byte)(_H[i] >> (8 * (3 - j))); - } - } - - return hash; - } - - public void Initialize() - { - count = 0; - _ProcessingBufferCount = 0; - - _H[0] = 0x67452301; - _H[1] = 0xefcdab89; - _H[2] = 0x98badcfe; - _H[3] = 0x10325476; - _H[4] = 0xC3D2E1F0; - } - - private void ProcessBlock(byte[] inputBuffer, uint inputOffset) - { - uint a, b, c, d, e; - - count += BLOCK_SIZE_BYTES; - - // abc removal would not work on the fields - uint[] _H = this._H; - uint[] buff = this.buff; - InitialiseBuff(buff, inputBuffer, inputOffset); - FillBuff(buff); - - a = _H[0]; - b = _H[1]; - c = _H[2]; - d = _H[3]; - e = _H[4]; - - // This function was unrolled because it seems to be doubling our performance with current compiler/VM. - // Possibly roll up if this changes. - - // ---- Round 1 -------- - int i = 0; - while (i < 20) - { - e += ((a << 5) | (a >> 27)) + (((c ^ d) & b) ^ d) + 0x5A827999 + buff[i]; - b = (b << 30) | (b >> 2); - - d += ((e << 5) | (e >> 27)) + (((b ^ c) & a) ^ c) + 0x5A827999 + buff[i + 1]; - a = (a << 30) | (a >> 2); - - c += ((d << 5) | (d >> 27)) + (((a ^ b) & e) ^ b) + 0x5A827999 + buff[i + 2]; - e = (e << 30) | (e >> 2); - - b += ((c << 5) | (c >> 27)) + (((e ^ a) & d) ^ a) + 0x5A827999 + buff[i + 3]; - d = (d << 30) | (d >> 2); - - a += ((b << 5) | (b >> 27)) + (((d ^ e) & c) ^ e) + 0x5A827999 + buff[i + 4]; - c = (c << 30) | (c >> 2); - i += 5; - } - - // ---- Round 2 -------- - while (i < 40) - { - e += ((a << 5) | (a >> 27)) + (b ^ c ^ d) + 0x6ED9EBA1 + buff[i]; - b = (b << 30) | (b >> 2); - - d += ((e << 5) | (e >> 27)) + (a ^ b ^ c) + 0x6ED9EBA1 + buff[i + 1]; - a = (a << 30) | (a >> 2); - - c += ((d << 5) | (d >> 27)) + (e ^ a ^ b) + 0x6ED9EBA1 + buff[i + 2]; - e = (e << 30) | (e >> 2); - - b += ((c << 5) | (c >> 27)) + (d ^ e ^ a) + 0x6ED9EBA1 + buff[i + 3]; - d = (d << 30) | (d >> 2); - - a += ((b << 5) | (b >> 27)) + (c ^ d ^ e) + 0x6ED9EBA1 + buff[i + 4]; - c = (c << 30) | (c >> 2); - i += 5; - } - - // ---- Round 3 -------- - while (i < 60) - { - e += ((a << 5) | (a >> 27)) + ((b & c) | (b & d) | (c & d)) + 0x8F1BBCDC + buff[i]; - b = (b << 30) | (b >> 2); - - d += ((e << 5) | (e >> 27)) + ((a & b) | (a & c) | (b & c)) + 0x8F1BBCDC + buff[i + 1]; - a = (a << 30) | (a >> 2); - - c += ((d << 5) | (d >> 27)) + ((e & a) | (e & b) | (a & b)) + 0x8F1BBCDC + buff[i + 2]; - e = (e << 30) | (e >> 2); - - b += ((c << 5) | (c >> 27)) + ((d & e) | (d & a) | (e & a)) + 0x8F1BBCDC + buff[i + 3]; - d = (d << 30) | (d >> 2); - - a += ((b << 5) | (b >> 27)) + ((c & d) | (c & e) | (d & e)) + 0x8F1BBCDC + buff[i + 4]; - c = (c << 30) | (c >> 2); - i += 5; - } - - // ---- Round 4 -------- - while (i < 80) - { - e += ((a << 5) | (a >> 27)) + (b ^ c ^ d) + 0xCA62C1D6 + buff[i]; - b = (b << 30) | (b >> 2); - - d += ((e << 5) | (e >> 27)) + (a ^ b ^ c) + 0xCA62C1D6 + buff[i + 1]; - a = (a << 30) | (a >> 2); - - c += ((d << 5) | (d >> 27)) + (e ^ a ^ b) + 0xCA62C1D6 + buff[i + 2]; - e = (e << 30) | (e >> 2); - - b += ((c << 5) | (c >> 27)) + (d ^ e ^ a) + 0xCA62C1D6 + buff[i + 3]; - d = (d << 30) | (d >> 2); - - a += ((b << 5) | (b >> 27)) + (c ^ d ^ e) + 0xCA62C1D6 + buff[i + 4]; - c = (c << 30) | (c >> 2); - i += 5; - } - - _H[0] += a; - _H[1] += b; - _H[2] += c; - _H[3] += d; - _H[4] += e; - } - - private static void InitialiseBuff(uint[] buff, byte[] input, uint inputOffset) - { - buff[0] = (uint)((input[inputOffset + 0] << 24) | (input[inputOffset + 1] << 16) | (input[inputOffset + 2] << 8) | (input[inputOffset + 3])); - buff[1] = (uint)((input[inputOffset + 4] << 24) | (input[inputOffset + 5] << 16) | (input[inputOffset + 6] << 8) | (input[inputOffset + 7])); - buff[2] = (uint)((input[inputOffset + 8] << 24) | (input[inputOffset + 9] << 16) | (input[inputOffset + 10] << 8) | (input[inputOffset + 11])); - buff[3] = (uint)((input[inputOffset + 12] << 24) | (input[inputOffset + 13] << 16) | (input[inputOffset + 14] << 8) | (input[inputOffset + 15])); - buff[4] = (uint)((input[inputOffset + 16] << 24) | (input[inputOffset + 17] << 16) | (input[inputOffset + 18] << 8) | (input[inputOffset + 19])); - buff[5] = (uint)((input[inputOffset + 20] << 24) | (input[inputOffset + 21] << 16) | (input[inputOffset + 22] << 8) | (input[inputOffset + 23])); - buff[6] = (uint)((input[inputOffset + 24] << 24) | (input[inputOffset + 25] << 16) | (input[inputOffset + 26] << 8) | (input[inputOffset + 27])); - buff[7] = (uint)((input[inputOffset + 28] << 24) | (input[inputOffset + 29] << 16) | (input[inputOffset + 30] << 8) | (input[inputOffset + 31])); - buff[8] = (uint)((input[inputOffset + 32] << 24) | (input[inputOffset + 33] << 16) | (input[inputOffset + 34] << 8) | (input[inputOffset + 35])); - buff[9] = (uint)((input[inputOffset + 36] << 24) | (input[inputOffset + 37] << 16) | (input[inputOffset + 38] << 8) | (input[inputOffset + 39])); - buff[10] = (uint)((input[inputOffset + 40] << 24) | (input[inputOffset + 41] << 16) | (input[inputOffset + 42] << 8) | (input[inputOffset + 43])); - buff[11] = (uint)((input[inputOffset + 44] << 24) | (input[inputOffset + 45] << 16) | (input[inputOffset + 46] << 8) | (input[inputOffset + 47])); - buff[12] = (uint)((input[inputOffset + 48] << 24) | (input[inputOffset + 49] << 16) | (input[inputOffset + 50] << 8) | (input[inputOffset + 51])); - buff[13] = (uint)((input[inputOffset + 52] << 24) | (input[inputOffset + 53] << 16) | (input[inputOffset + 54] << 8) | (input[inputOffset + 55])); - buff[14] = (uint)((input[inputOffset + 56] << 24) | (input[inputOffset + 57] << 16) | (input[inputOffset + 58] << 8) | (input[inputOffset + 59])); - buff[15] = (uint)((input[inputOffset + 60] << 24) | (input[inputOffset + 61] << 16) | (input[inputOffset + 62] << 8) | (input[inputOffset + 63])); - } - - private static void FillBuff(uint[] buff) - { - uint val; - for (int i = 16; i < 80; i += 8) - { - val = buff[i - 3] ^ buff[i - 8] ^ buff[i - 14] ^ buff[i - 16]; - buff[i] = (val << 1) | (val >> 31); - - val = buff[i - 2] ^ buff[i - 7] ^ buff[i - 13] ^ buff[i - 15]; - buff[i + 1] = (val << 1) | (val >> 31); - - val = buff[i - 1] ^ buff[i - 6] ^ buff[i - 12] ^ buff[i - 14]; - buff[i + 2] = (val << 1) | (val >> 31); - - val = buff[i + 0] ^ buff[i - 5] ^ buff[i - 11] ^ buff[i - 13]; - buff[i + 3] = (val << 1) | (val >> 31); - - val = buff[i + 1] ^ buff[i - 4] ^ buff[i - 10] ^ buff[i - 12]; - buff[i + 4] = (val << 1) | (val >> 31); - - val = buff[i + 2] ^ buff[i - 3] ^ buff[i - 9] ^ buff[i - 11]; - buff[i + 5] = (val << 1) | (val >> 31); - - val = buff[i + 3] ^ buff[i - 2] ^ buff[i - 8] ^ buff[i - 10]; - buff[i + 6] = (val << 1) | (val >> 31); - - val = buff[i + 4] ^ buff[i - 1] ^ buff[i - 7] ^ buff[i - 9]; - buff[i + 7] = (val << 1) | (val >> 31); - } - } - - private void ProcessFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) - { - ulong total = count + (ulong)inputCount; - int paddingSize = (56 - (int)(total % BLOCK_SIZE_BYTES)); - - if (paddingSize < 1) - paddingSize += BLOCK_SIZE_BYTES; - - int length = inputCount + paddingSize + 8; - byte[] fooBuffer = (length == 64) ? _ProcessingBuffer : new byte[length]; - - for (int i = 0; i < inputCount; i++) - { - fooBuffer[i] = inputBuffer[i + inputOffset]; - } - - fooBuffer[inputCount] = 0x80; - for (int i = inputCount + 1; i < inputCount + paddingSize; i++) - { - fooBuffer[i] = 0x00; - } - - // I deal in bytes. The algorithm deals in bits. - ulong size = total << 3; - AddLength(size, fooBuffer, inputCount + paddingSize); - ProcessBlock(fooBuffer, 0); - - if (length == 128) - ProcessBlock(fooBuffer, 64); - } - - internal void AddLength(ulong length, byte[] buffer, int position) - { - buffer[position++] = (byte)(length >> 56); - buffer[position++] = (byte)(length >> 48); - buffer[position++] = (byte)(length >> 40); - buffer[position++] = (byte)(length >> 32); - buffer[position++] = (byte)(length >> 24); - buffer[position++] = (byte)(length >> 16); - buffer[position++] = (byte)(length >> 8); - buffer[position] = (byte)(length); - } - } - - public sealed class SHA1CryptoServiceProvider : SHA1 - { - - private SHA1Internal sha; - - public SHA1CryptoServiceProvider() - { - sha = new SHA1Internal(); - } - - ~SHA1CryptoServiceProvider() - { - Dispose(false); - } - - protected override void Dispose(bool disposing) - { - // nothing new to do (managed implementation) - base.Dispose(disposing); - } - - protected override void HashCore(byte[] rgb, int ibStart, int cbSize) - { - State = 1; - sha.HashCore(rgb, ibStart, cbSize); - } - - protected override byte[] HashFinal() - { - State = 0; - return sha.HashFinal(); - } - - public override void Initialize() - { - sha.Initialize(); - } - } - - public abstract class SHA1 : HashAlgorithm - { - protected SHA1() - { - HashSizeValue = 160; - } - } - - public abstract class HashAlgorithm : IDisposable - { - protected int HashSizeValue; - protected internal byte[] HashValue; - protected int State = 0; - - private bool m_bDisposed = false; - - protected HashAlgorithm() { } - - // - // public properties - // - - public virtual int HashSize - { - get { return HashSizeValue; } - } - - // - // public methods - // - - public byte[] ComputeHash(Stream inputStream) - { - if (m_bDisposed) - throw new ObjectDisposedException(null); - - // Default the buffer size to 4K. - byte[] buffer = new byte[4096]; - int bytesRead; - do - { - bytesRead = inputStream.Read(buffer, 0, 4096); - if (bytesRead > 0) - { - HashCore(buffer, 0, bytesRead); - } - } while (bytesRead > 0); - - HashValue = HashFinal(); - byte[] Tmp = (byte[])HashValue.Clone(); - Initialize(); - return (Tmp); - } - - public byte[] ComputeHash(byte[] buffer) - { - if (m_bDisposed) - throw new ObjectDisposedException(null); - - // Do some validation - if (buffer == null) throw new ArgumentNullException("buffer"); - - HashCore(buffer, 0, buffer.Length); - HashValue = HashFinal(); - byte[] Tmp = (byte[])HashValue.Clone(); - Initialize(); - return (Tmp); - } - - // ICryptoTransform methods - - // we assume any HashAlgorithm can take input a byte at a time - public virtual int InputBlockSize - { - get { return (1); } - } - - public virtual int OutputBlockSize - { - get { return (1); } - } - - public virtual bool CanTransformMultipleBlocks - { - get { return (true); } - } - - public virtual bool CanReuseTransform - { - get { return (true); } - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - public void Clear() - { - (this as IDisposable).Dispose(); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - if (HashValue != null) - Array.Clear(HashValue, 0, HashValue.Length); - HashValue = null; - m_bDisposed = true; - } - } - - // - // abstract public methods - // - - public abstract void Initialize(); - - protected abstract void HashCore(byte[] array, int ibStart, int cbSize); - - protected abstract byte[] HashFinal(); - } -} diff --git a/Storage/Storage/Internal_/File/State/FileState.cs b/Storage/Storage/Internal_/File/State/FileState.cs deleted file mode 100644 index cb619aa..0000000 --- a/Storage/Storage/Internal_/File/State/FileState.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; - -namespace LeanCloud.Storage.Internal { - public class FileState { - public string ObjectId { get; internal set; } - public string Name { get; internal set; } - public string CloudName { get; set; } - public string MimeType { get; internal set; } - public Uri Url { get; internal set; } - public IDictionary MetaData { get; internal set; } - public long Size { get; internal set; } - public long FixedChunkSize { get; internal set; } - - public int counter; - public Stream frozenData; - public string bucketId; - public string bucket; - public string token; - public long completed; - public List block_ctxes = new List(); - - public static FileState NewFromDict(IDictionary dict) { - FileState state = new FileState { - ObjectId = dict["objectId"] as string, - Url = new Uri(dict["url"] as string, UriKind.Absolute) - }; - if (dict.TryGetValue("name", out object name)) { - state.Name = name as string; - } - if (dict.TryGetValue("metaData", out object metaData)) { - state.MetaData = metaData as Dictionary; - } - return state; - } - } -} diff --git a/Storage/Storage/Internal_/InstallationId/Controller/InstallationIdController.cs b/Storage/Storage/Internal_/InstallationId/Controller/InstallationIdController.cs deleted file mode 100644 index b722d4c..0000000 --- a/Storage/Storage/Internal_/InstallationId/Controller/InstallationIdController.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.IO; - -namespace LeanCloud.Storage.Internal { - /// - /// 临时方案,后面将有 Android 和 iOS 提供 device token - /// - public class InstallationIdController { - private string installationId; - private readonly object mutex = new object(); - - public string Get() { - if (installationId == null) { - lock (mutex) { - if (installationId == null) { - string installationPath = "installation.conf"; - // 文件读取或从 Native 平台读取 - if (System.IO.File.Exists(installationPath)) { - using (StreamReader reader = new StreamReader(installationPath)) { - installationId = reader.ReadToEnd(); - if (installationId != null) { - return installationId; - } - } - } - // 生成新的 device token - Guid newInstallationId = Guid.NewGuid(); - installationId = newInstallationId.ToString(); - // 写回文件 - using (StreamWriter writer = new StreamWriter(installationPath)) { - writer.Write(installationId); - } - } - } - } - return installationId; - } - } -} diff --git a/Storage/Storage/Internal_/Object/State/IObjectState.cs b/Storage/Storage/Internal_/Object/State/IObjectState.cs deleted file mode 100644 index a22a4e3..0000000 --- a/Storage/Storage/Internal_/Object/State/IObjectState.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace LeanCloud.Storage.Internal { - public interface IObjectState : IEnumerable> { - string ClassName { get; } - string ObjectId { get; } - AVACL ACL { get; set; } - DateTime? UpdatedAt { get; } - DateTime? CreatedAt { get; } - object this[string key] { get; } - - bool ContainsKey(string key); - - IObjectState MutatedClone(Action func); - } -} diff --git a/Storage/Storage/Internal_/Object/State/MutableObjectState.cs b/Storage/Storage/Internal_/Object/State/MutableObjectState.cs deleted file mode 100644 index c141d95..0000000 --- a/Storage/Storage/Internal_/Object/State/MutableObjectState.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace LeanCloud.Storage.Internal { - public class MutableObjectState : IObjectState { - public string ClassName { get; set; } - public string ObjectId { get; set; } - public AVACL ACL { get; set; } - public DateTime? UpdatedAt { get; set; } - public DateTime? CreatedAt { get; set; } - - public IDictionary ServerData { - get; set; - } = new Dictionary(); - - public object this[string key] { - get { - return ServerData[key]; - } - } - - public bool ContainsKey(string key) { - return ServerData.ContainsKey(key); - } - - public void Apply(IDictionary operationSet) { - // Apply operationSet - foreach (var pair in operationSet) { - ServerData.TryGetValue(pair.Key, out object oldValue); - var newValue = pair.Value.Apply(oldValue, pair.Key); - if (newValue != AVDeleteOperation.DeleteToken) { - ServerData[pair.Key] = newValue; - } else { - ServerData.Remove(pair.Key); - } - } - } - - public void Apply(IObjectState other) { - if (other.ObjectId != null) { - ObjectId = other.ObjectId; - } - if (other.ACL != null) { - ACL = other.ACL; - } - if (other.UpdatedAt != null) { - UpdatedAt = other.UpdatedAt; - } - if (other.CreatedAt != null) { - CreatedAt = other.CreatedAt; - } - - foreach (var pair in other) { - ServerData[pair.Key] = pair.Value; - } - } - - public IObjectState MutatedClone(Action func) { - var clone = MutableClone(); - func(clone); - return clone; - } - - protected virtual MutableObjectState MutableClone() { - return new MutableObjectState { - ClassName = ClassName, - ObjectId = ObjectId, - CreatedAt = CreatedAt, - UpdatedAt = UpdatedAt, - ServerData = new Dictionary(ServerData) - }; - } - - IEnumerator> IEnumerable>.GetEnumerator() { - return ServerData.GetEnumerator(); - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { - return ((IEnumerable>)this).GetEnumerator(); - } - } -} diff --git a/Storage/Storage/Internal_/Object/State/ObjectData.cs b/Storage/Storage/Internal_/Object/State/ObjectData.cs deleted file mode 100644 index 730adac..0000000 --- a/Storage/Storage/Internal_/Object/State/ObjectData.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; - -namespace LeanCloud.Storage.Internal { - internal class ObjectData : IEnumerable> { - internal string ClassName { - get; set; - } - - internal string ObjectId { - get; set; - } - - internal AVACL ACL { - get; set; - } - - internal DateTime? CreatedAt { - get; set; - } - - internal DateTime? UpdatedAt { - get; set; - } - - internal Dictionary CustomProperties { - get; set; - } = new Dictionary(); - - internal object this[string key] { - get { - return CustomProperties[key]; - } - } - - internal bool ContainsKey(string key) { - return CustomProperties.ContainsKey(key); - } - - internal void Apply(Dictionary operations) { - foreach (KeyValuePair entry in operations) { - string propKey = entry.Key; - object propVal = entry.Value; - if (!CustomProperties.TryGetValue(propKey, out object oldVal)) { - continue; - } - object newVal = entry.Value.Apply(oldVal, propKey); - if (newVal == AVDeleteOperation.DeleteToken) { - CustomProperties.Remove(propKey); - } else { - CustomProperties[propKey] = newVal; - } - } - } - - #region IEnumerable - - public IEnumerator> GetEnumerator() { - return ((IEnumerable>)CustomProperties).GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() { - return ((IEnumerable>)CustomProperties).GetEnumerator(); - } - - #endregion - } -} diff --git a/Storage/Storage/Internal_/Object/Subclassing/ObjectSubclassInfo.cs b/Storage/Storage/Internal_/Object/Subclassing/ObjectSubclassInfo.cs deleted file mode 100644 index 95be34c..0000000 --- a/Storage/Storage/Internal_/Object/Subclassing/ObjectSubclassInfo.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; - -namespace LeanCloud.Storage.Internal { - internal class ObjectSubclassInfo { - public ObjectSubclassInfo(Type type, ConstructorInfo constructor) { - TypeInfo = type.GetTypeInfo(); - ClassName = GetClassName(TypeInfo); - Constructor = constructor; - PropertyMappings = ReflectionHelpers.GetProperties(type) - .Select(prop => Tuple.Create(prop, prop.GetCustomAttribute(true))) - .Where(t => t.Item2 != null) - .Select(t => Tuple.Create(t.Item1, t.Item2.FieldName)) - .ToDictionary(t => t.Item1.Name, t => t.Item2); - } - - public TypeInfo TypeInfo { get; private set; } - public string ClassName { get; private set; } - public IDictionary PropertyMappings { get; private set; } - private ConstructorInfo Constructor { get; set; } - - public AVObject Instantiate() { - return (AVObject)Constructor.Invoke(null); - } - - internal static string GetClassName(TypeInfo type) { - var attribute = type.GetCustomAttribute(); - return attribute?.ClassName; - } - } -} diff --git a/Storage/Storage/Internal_/Object/Subclassing/ObjectSubclassingController.cs b/Storage/Storage/Internal_/Object/Subclassing/ObjectSubclassingController.cs deleted file mode 100644 index 3458459..0000000 --- a/Storage/Storage/Internal_/Object/Subclassing/ObjectSubclassingController.cs +++ /dev/null @@ -1,134 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Threading; - -namespace LeanCloud.Storage.Internal { - public class ObjectSubclassingController { - // Class names starting with _ are documented to be reserved. Use this one - // here to allow us to 'inherit' certain properties. - private static readonly string avObjectClassName = "_AVObject"; - - private readonly ReaderWriterLockSlim mutex; - private readonly IDictionary registeredSubclasses; - private readonly Dictionary registerActions; - - public ObjectSubclassingController() { - mutex = new ReaderWriterLockSlim(); - registeredSubclasses = new Dictionary(); - registerActions = new Dictionary(); - - // Register the AVObject subclass, so we get access to the ACL, - // objectId, and other AVFieldName properties. - RegisterSubclass(typeof(AVObject)); - } - - public string GetClassName(Type type) { - return type == typeof(AVObject) - ? avObjectClassName - : ObjectSubclassInfo.GetClassName(type.GetTypeInfo()); - } - - public Type GetType(string className) { - mutex.EnterReadLock(); - registeredSubclasses.TryGetValue(className, out ObjectSubclassInfo info); - mutex.ExitReadLock(); - - return info?.TypeInfo.AsType(); - } - - public bool IsTypeValid(string className, Type type) { - mutex.EnterReadLock(); - registeredSubclasses.TryGetValue(className, out ObjectSubclassInfo subclassInfo); - mutex.ExitReadLock(); - - return subclassInfo == null - ? type == typeof(AVObject) - : subclassInfo.TypeInfo == type.GetTypeInfo(); - } - - public void RegisterSubclass(Type type) { - TypeInfo typeInfo = type.GetTypeInfo(); - if (!typeof(AVObject).GetTypeInfo().IsAssignableFrom(typeInfo)) { - throw new ArgumentException("Cannot register a type that is not a subclass of AVObject"); - } - - string className = GetClassName(type); - - try { - // Perform this as a single independent transaction, so we can never get into an - // intermediate state where we *theoretically* register the wrong class due to a - // TOCTTOU bug. - mutex.EnterWriteLock(); - - if (registeredSubclasses.TryGetValue(className, out ObjectSubclassInfo previousInfo)) { - if (typeInfo.IsAssignableFrom(previousInfo.TypeInfo)) { - // Previous subclass is more specific or equal to the current type, do nothing. - return; - } - if (previousInfo.TypeInfo.IsAssignableFrom(typeInfo)) { - // Previous subclass is parent of new child, fallthrough and actually register - // this class. - /* Do nothing */ - } else { - throw new ArgumentException( - "Tried to register both " + previousInfo.TypeInfo.FullName + " and " + typeInfo.FullName + - " as the AVObject subclass of " + className + ". Cannot determine the right class " + - "to use because neither inherits from the other." - ); - } - } - - ConstructorInfo constructor = type.FindConstructor(); - if (constructor == null) { - throw new ArgumentException("Cannot register a type that does not implement the default constructor!"); - } - - registeredSubclasses[className] = new ObjectSubclassInfo(type, constructor); - } finally { - mutex.ExitWriteLock(); - } - - - mutex.EnterReadLock(); - registerActions.TryGetValue(className, out Action toPerform); - mutex.ExitReadLock(); - - toPerform?.Invoke(); - } - - public void UnregisterSubclass(Type type) { - mutex.EnterWriteLock(); - registeredSubclasses.Remove(GetClassName(type)); - mutex.ExitWriteLock(); - } - - public void AddRegisterHook(Type t, Action action) { - mutex.EnterWriteLock(); - registerActions.Add(GetClassName(t), action); - mutex.ExitWriteLock(); - } - - public AVObject Instantiate(string className) { - - mutex.EnterReadLock(); - registeredSubclasses.TryGetValue(className, out ObjectSubclassInfo info); - mutex.ExitReadLock(); - - return info != null - ? info.Instantiate() - : new AVObject(className); - } - - public IDictionary GetPropertyMappings(string className) { - mutex.EnterReadLock(); - if (!registeredSubclasses.TryGetValue(className, out ObjectSubclassInfo info)) { - _ = registeredSubclasses.TryGetValue(avObjectClassName, out info); - } - mutex.ExitReadLock(); - - return info.PropertyMappings; - } - - } -} diff --git a/Storage/Storage/Internal_/Operation/AVAddOperation.cs b/Storage/Storage/Internal_/Operation/AVAddOperation.cs deleted file mode 100644 index 6f836e4..0000000 --- a/Storage/Storage/Internal_/Operation/AVAddOperation.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using LeanCloud.Utilities; - -namespace LeanCloud.Storage.Internal { - public class AVAddOperation : IAVFieldOperation { - private readonly ReadOnlyCollection objects; - - public AVAddOperation(IEnumerable objects) { - this.objects = new ReadOnlyCollection(objects.ToList()); - } - - public object Encode() { - return new Dictionary { - { "__op", "Add" }, - { "objects", PointerOrLocalIdEncoder.Instance.Encode(objects) } - }; - } - - public IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous) { - if (previous == null) { - return this; - } - if (previous is AVDeleteOperation) { - return new AVSetOperation(objects.ToList()); - } - if (previous is AVSetOperation setOp) { - var oldList = Conversion.To>(setOp.Value); - return new AVSetOperation(oldList.Concat(objects).ToList()); - } - if (previous is AVAddOperation) { - return new AVAddOperation(((AVAddOperation)previous).Objects.Concat(objects)); - } - throw new InvalidOperationException("Operation is invalid after previous operation."); - } - - public object Apply(object oldValue, string key) { - if (oldValue == null) { - return objects.ToList(); - } - var oldList = Conversion.To>(oldValue); - return oldList.Concat(objects).ToList(); - } - - public IEnumerable Objects { - get { - return objects; - } - } - } -} diff --git a/Storage/Storage/Internal_/Operation/AVAddUniqueOperation.cs b/Storage/Storage/Internal_/Operation/AVAddUniqueOperation.cs deleted file mode 100644 index 6a820e9..0000000 --- a/Storage/Storage/Internal_/Operation/AVAddUniqueOperation.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using LeanCloud.Utilities; - -namespace LeanCloud.Storage.Internal { - public class AVAddUniqueOperation : IAVFieldOperation { - private ReadOnlyCollection objects; - public AVAddUniqueOperation(IEnumerable objects) { - this.objects = new ReadOnlyCollection(objects.Distinct().ToList()); - } - - public object Encode() { - return new Dictionary { - { "__op", "AddUnique" }, - { "objects", PointerOrLocalIdEncoder.Instance.Encode(objects) } - }; - } - - public IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous) { - if (previous == null) { - return this; - } - if (previous is AVDeleteOperation) { - return new AVSetOperation(objects.ToList()); - } - if (previous is AVSetOperation setOp) { - var oldList = Conversion.To>(setOp.Value); - var result = this.Apply(oldList, null); - return new AVSetOperation(result); - } - if (previous is AVAddUniqueOperation) { - var oldList = ((AVAddUniqueOperation)previous).Objects; - return new AVAddUniqueOperation((IList)this.Apply(oldList, null)); - } - throw new InvalidOperationException("Operation is invalid after previous operation."); - } - - public object Apply(object oldValue, string key) { - if (oldValue == null) { - return objects.ToList(); - } - var newList = Conversion.To>(oldValue).ToList(); - var comparer = AVFieldOperations.AVObjectComparer; - foreach (var objToAdd in objects) { - if (objToAdd is AVObject) { - var matchedObj = newList.FirstOrDefault(listObj => comparer.Equals(objToAdd, listObj)); - if (matchedObj == null) { - newList.Add(objToAdd); - } else { - var index = newList.IndexOf(matchedObj); - newList[index] = objToAdd; - } - } else if (!newList.Contains(objToAdd, comparer)) { - newList.Add(objToAdd); - } - } - return newList; - } - - public IEnumerable Objects { - get { - return objects; - } - } - } -} diff --git a/Storage/Storage/Internal_/Operation/AVDeleteOperation.cs b/Storage/Storage/Internal_/Operation/AVDeleteOperation.cs deleted file mode 100644 index 6b548ae..0000000 --- a/Storage/Storage/Internal_/Operation/AVDeleteOperation.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Collections.Generic; - -namespace LeanCloud.Storage.Internal { - /// - /// An operation where a field is deleted from the object. - /// - public class AVDeleteOperation : IAVFieldOperation { - internal static readonly object DeleteToken = new object(); - private static AVDeleteOperation _Instance = new AVDeleteOperation(); - - public static AVDeleteOperation Instance { - get { - return _Instance; - } - } - - private AVDeleteOperation() { } - - public object Encode() { - return new Dictionary { - {"__op", "Delete"} - }; - } - - public IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous) { - return this; - } - - public object Apply(object oldValue, string key) { - return DeleteToken; - } - } -} diff --git a/Storage/Storage/Internal_/Operation/AVFieldOperations.cs b/Storage/Storage/Internal_/Operation/AVFieldOperations.cs deleted file mode 100644 index 39a2d6a..0000000 --- a/Storage/Storage/Internal_/Operation/AVFieldOperations.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace LeanCloud.Storage.Internal { - public class AVObjectIdComparer : IEqualityComparer { - bool IEqualityComparer.Equals(object p1, object p2) { - if (p1 is AVObject avObj1 && p2 is AVObject avObj2) { - return object.Equals(avObj1.ObjectId, avObj2.ObjectId); - } - return object.Equals(p1, p2); - } - - public int GetHashCode(object p) { - if (p is AVObject avObject) { - return avObject.ObjectId.GetHashCode(); - } - return p.GetHashCode(); - } - } - - static class AVFieldOperations { - private static AVObjectIdComparer comparer; - - public static IAVFieldOperation Decode(IDictionary json) { - throw new NotImplementedException(); - } - - public static IEqualityComparer AVObjectComparer { - get { - if (comparer == null) { - comparer = new AVObjectIdComparer(); - } - return comparer; - } - } - } -} diff --git a/Storage/Storage/Internal_/Operation/AVIncrementOperation.cs b/Storage/Storage/Internal_/Operation/AVIncrementOperation.cs deleted file mode 100644 index b0922f1..0000000 --- a/Storage/Storage/Internal_/Operation/AVIncrementOperation.cs +++ /dev/null @@ -1,147 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace LeanCloud.Storage.Internal { - public class AVIncrementOperation : IAVFieldOperation { - private static readonly IDictionary, Func> adders; - - static AVIncrementOperation() { - // Defines adders for all of the implicit conversions: http://msdn.microsoft.com/en-US/library/y5b434w4(v=vs.80).aspx - adders = new Dictionary, Func> { - {new Tuple(typeof(sbyte), typeof(sbyte)), (left, right) => (sbyte)left + (sbyte)right}, - {new Tuple(typeof(sbyte), typeof(short)), (left, right) => (sbyte)left + (short)right}, - {new Tuple(typeof(sbyte), typeof(int)), (left, right) => (sbyte)left + (int)right}, - {new Tuple(typeof(sbyte), typeof(long)), (left, right) => (sbyte)left + (long)right}, - {new Tuple(typeof(sbyte), typeof(float)), (left, right) => (sbyte)left + (float)right}, - {new Tuple(typeof(sbyte), typeof(double)), (left, right) => (sbyte)left + (double)right}, - {new Tuple(typeof(sbyte), typeof(decimal)), (left, right) => (sbyte)left + (decimal)right}, - {new Tuple(typeof(byte), typeof(byte)), (left, right) => (byte)left + (byte)right}, - {new Tuple(typeof(byte), typeof(short)), (left, right) => (byte)left + (short)right}, - {new Tuple(typeof(byte), typeof(ushort)), (left, right) => (byte)left + (ushort)right}, - {new Tuple(typeof(byte), typeof(int)), (left, right) => (byte)left + (int)right}, - {new Tuple(typeof(byte), typeof(uint)), (left, right) => (byte)left + (uint)right}, - {new Tuple(typeof(byte), typeof(long)), (left, right) => (byte)left + (long)right}, - {new Tuple(typeof(byte), typeof(ulong)), (left, right) => (byte)left + (ulong)right}, - {new Tuple(typeof(byte), typeof(float)), (left, right) => (byte)left + (float)right}, - {new Tuple(typeof(byte), typeof(double)), (left, right) => (byte)left + (double)right}, - {new Tuple(typeof(byte), typeof(decimal)), (left, right) => (byte)left + (decimal)right}, - {new Tuple(typeof(short), typeof(short)), (left, right) => (short)left + (short)right}, - {new Tuple(typeof(short), typeof(int)), (left, right) => (short)left + (int)right}, - {new Tuple(typeof(short), typeof(long)), (left, right) => (short)left + (long)right}, - {new Tuple(typeof(short), typeof(float)), (left, right) => (short)left + (float)right}, - {new Tuple(typeof(short), typeof(double)), (left, right) => (short)left + (double)right}, - {new Tuple(typeof(short), typeof(decimal)), (left, right) => (short)left + (decimal)right}, - {new Tuple(typeof(ushort), typeof(ushort)), (left, right) => (ushort)left + (ushort)right}, - {new Tuple(typeof(ushort), typeof(int)), (left, right) => (ushort)left + (int)right}, - {new Tuple(typeof(ushort), typeof(uint)), (left, right) => (ushort)left + (uint)right}, - {new Tuple(typeof(ushort), typeof(long)), (left, right) => (ushort)left + (long)right}, - {new Tuple(typeof(ushort), typeof(ulong)), (left, right) => (ushort)left + (ulong)right}, - {new Tuple(typeof(ushort), typeof(float)), (left, right) => (ushort)left + (float)right}, - {new Tuple(typeof(ushort), typeof(double)), (left, right) => (ushort)left + (double)right}, - {new Tuple(typeof(ushort), typeof(decimal)), (left, right) => (ushort)left + (decimal)right}, - {new Tuple(typeof(int), typeof(int)), (left, right) => (int)left + (int)right}, - {new Tuple(typeof(int), typeof(long)), (left, right) => (int)left + (long)right}, - {new Tuple(typeof(int), typeof(float)), (left, right) => (int)left + (float)right}, - {new Tuple(typeof(int), typeof(double)), (left, right) => (int)left + (double)right}, - {new Tuple(typeof(int), typeof(decimal)), (left, right) => (int)left + (decimal)right}, - {new Tuple(typeof(uint), typeof(uint)), (left, right) => (uint)left + (uint)right}, - {new Tuple(typeof(uint), typeof(long)), (left, right) => (uint)left + (long)right}, - {new Tuple(typeof(uint), typeof(ulong)), (left, right) => (uint)left + (ulong)right}, - {new Tuple(typeof(uint), typeof(float)), (left, right) => (uint)left + (float)right}, - {new Tuple(typeof(uint), typeof(double)), (left, right) => (uint)left + (double)right}, - {new Tuple(typeof(uint), typeof(decimal)), (left, right) => (uint)left + (decimal)right}, - {new Tuple(typeof(long), typeof(long)), (left, right) => (long)left + (long)right}, - {new Tuple(typeof(long), typeof(float)), (left, right) => (long)left + (float)right}, - {new Tuple(typeof(long), typeof(double)), (left, right) => (long)left + (double)right}, - {new Tuple(typeof(long), typeof(decimal)), (left, right) => (long)left + (decimal)right}, - {new Tuple(typeof(char), typeof(char)), (left, right) => (char)left + (char)right}, - {new Tuple(typeof(char), typeof(ushort)), (left, right) => (char)left + (ushort)right}, - {new Tuple(typeof(char), typeof(int)), (left, right) => (char)left + (int)right}, - {new Tuple(typeof(char), typeof(uint)), (left, right) => (char)left + (uint)right}, - {new Tuple(typeof(char), typeof(long)), (left, right) => (char)left + (long)right}, - {new Tuple(typeof(char), typeof(ulong)), (left, right) => (char)left + (ulong)right}, - {new Tuple(typeof(char), typeof(float)), (left, right) => (char)left + (float)right}, - {new Tuple(typeof(char), typeof(double)), (left, right) => (char)left + (double)right}, - {new Tuple(typeof(char), typeof(decimal)), (left, right) => (char)left + (decimal)right}, - {new Tuple(typeof(float), typeof(float)), (left, right) => (float)left + (float)right}, - {new Tuple(typeof(float), typeof(double)), (left, right) => (float)left + (double)right}, - {new Tuple(typeof(ulong), typeof(ulong)), (left, right) => (ulong)left + (ulong)right}, - {new Tuple(typeof(ulong), typeof(float)), (left, right) => (ulong)left + (float)right}, - {new Tuple(typeof(ulong), typeof(double)), (left, right) => (ulong)left + (double)right}, - {new Tuple(typeof(ulong), typeof(decimal)), (left, right) => (ulong)left + (decimal)right}, - {new Tuple(typeof(double), typeof(double)), (left, right) => (double)left + (double)right}, - {new Tuple(typeof(decimal), typeof(decimal)), (left, right) => (decimal)left + (decimal)right} - }; - // Generate the adders in the other direction - foreach (var pair in adders.Keys.ToList()) { - if (pair.Item1.Equals(pair.Item2)) { - continue; - } - var reversePair = new Tuple(pair.Item2, pair.Item1); - var func = adders[pair]; - adders[reversePair] = (left, right) => func(right, left); - } - } - - private object amount; - - public AVIncrementOperation(object amount) { - this.amount = amount; - } - - public object Encode() { - return new Dictionary - { - {"__op", "Increment"}, - {"amount", amount} - }; - } - - private static object Add(object obj1, object obj2) { - Func adder; - if (adders.TryGetValue(new Tuple(obj1.GetType(), obj2.GetType()), out adder)) { - return adder(obj1, obj2); - } - throw new InvalidCastException("Cannot add " + obj1.GetType() + " to " + obj2.GetType()); - } - - public IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous) { - if (previous == null) { - return this; - } - if (previous is AVDeleteOperation) { - return new AVSetOperation(amount); - } - if (previous is AVSetOperation) { - var otherAmount = ((AVSetOperation)previous).Value; - if (otherAmount is string) { - throw new InvalidOperationException("Cannot increment a non-number type."); - } - var myAmount = amount; - return new AVSetOperation(Add(otherAmount, myAmount)); - } - if (previous is AVIncrementOperation) { - object otherAmount = ((AVIncrementOperation)previous).Amount; - object myAmount = amount; - return new AVIncrementOperation(Add(otherAmount, myAmount)); - } - throw new InvalidOperationException("Operation is invalid after previous operation."); - } - - public object Apply(object oldValue, string key) { - if (oldValue is string) { - throw new InvalidOperationException("Cannot increment a non-number type."); - } - object otherAmount = oldValue ?? 0; - object myAmount = amount; - return Add(otherAmount, myAmount); - } - - public object Amount { - get { - return amount; - } - } - } -} diff --git a/Storage/Storage/Internal_/Operation/AVRelationOperation.cs b/Storage/Storage/Internal_/Operation/AVRelationOperation.cs deleted file mode 100644 index dab4a5f..0000000 --- a/Storage/Storage/Internal_/Operation/AVRelationOperation.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal { - public class AVRelationOperation : IAVFieldOperation { - private readonly IList adds; - private readonly IList removes; - private readonly string targetClassName; - - private AVRelationOperation(IEnumerable adds, - IEnumerable removes, - string targetClassName) { - this.targetClassName = targetClassName; - this.adds = new ReadOnlyCollection(adds.ToList()); - this.removes = new ReadOnlyCollection(removes.ToList()); - } - - public AVRelationOperation(IEnumerable adds, - IEnumerable removes) { - adds = adds ?? new AVObject[0]; - removes = removes ?? new AVObject[0]; - this.targetClassName = adds.Concat(removes).Select(o => o.ClassName).FirstOrDefault(); - this.adds = new ReadOnlyCollection(IdsFromObjects(adds).ToList()); - this.removes = new ReadOnlyCollection(IdsFromObjects(removes).ToList()); - } - - public object Encode() { - var adds = this.adds - .Select(id => PointerOrLocalIdEncoder.Instance.Encode( - AVObject.CreateWithoutData(targetClassName, id))) - .ToList(); - var removes = this.removes - .Select(id => PointerOrLocalIdEncoder.Instance.Encode( - AVObject.CreateWithoutData(targetClassName, id))) - .ToList(); - var addDict = adds.Count == 0 ? null : new Dictionary { - {"__op", "AddRelation"}, - {"objects", adds} - }; - var removeDict = removes.Count == 0 ? null : new Dictionary { - {"__op", "RemoveRelation"}, - {"objects", removes} - }; - - if (addDict != null && removeDict != null) { - return new Dictionary { - {"__op", "Batch"}, - {"ops", new[] {addDict, removeDict}} - }; - } - return addDict ?? removeDict; - } - - public IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous) { - if (previous == null) { - return this; - } - if (previous is AVDeleteOperation) { - throw new InvalidOperationException("You can't modify a relation after deleting it."); - } - var other = previous as AVRelationOperation; - if (other != null) { - if (other.TargetClassName != TargetClassName) { - throw new InvalidOperationException( - string.Format("Related object must be of class {0}, but {1} was passed in.", - other.TargetClassName, - TargetClassName)); - } - var newAdd = adds.Union(other.adds.Except(removes)).ToList(); - var newRemove = removes.Union(other.removes.Except(adds)).ToList(); - return new AVRelationOperation(newAdd, newRemove, TargetClassName); - } - throw new InvalidOperationException("Operation is invalid after previous operation."); - } - - public object Apply(object oldValue, string key) { - if (adds.Count == 0 && removes.Count == 0) { - return null; - } - if (oldValue == null) { - return AVRelationBase.CreateRelation(null, key, targetClassName); - } - if (oldValue is AVRelationBase) { - var oldRelation = (AVRelationBase)oldValue; - var oldClassName = oldRelation.TargetClassName; - if (oldClassName != null && oldClassName != targetClassName) { - throw new InvalidOperationException("Related object must be a " + oldClassName - + ", but a " + targetClassName + " was passed in."); - } - oldRelation.TargetClassName = targetClassName; - return oldRelation; - } - throw new InvalidOperationException("Operation is invalid after previous operation."); - } - - public string TargetClassName { get { return targetClassName; } } - - private IEnumerable IdsFromObjects(IEnumerable objects) { - foreach (var obj in objects) { - if (obj.ObjectId == null) { - throw new ArgumentException( - "You can't add an unsaved AVObject to a relation."); - } - if (obj.ClassName != targetClassName) { - throw new ArgumentException(string.Format( - "Tried to create a AVRelation with 2 different types: {0} and {1}", - targetClassName, - obj.ClassName)); - } - } - return objects.Select(o => o.ObjectId).Distinct(); - } - } -} diff --git a/Storage/Storage/Internal_/Operation/AVRemoveOperation.cs b/Storage/Storage/Internal_/Operation/AVRemoveOperation.cs deleted file mode 100644 index 65c35e7..0000000 --- a/Storage/Storage/Internal_/Operation/AVRemoveOperation.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; - -using LeanCloud.Utilities; - -namespace LeanCloud.Storage.Internal { - public class AVRemoveOperation : IAVFieldOperation { - private ReadOnlyCollection objects; - public AVRemoveOperation(IEnumerable objects) { - this.objects = new ReadOnlyCollection(objects.Distinct().ToList()); - } - - public object Encode() { - return new Dictionary { - { "__op", "Remove" }, - { "objects", PointerOrLocalIdEncoder.Instance.Encode(objects) } - }; - } - - public IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous) { - if (previous == null) { - return this; - } - if (previous is AVDeleteOperation) { - return previous; - } - if (previous is AVSetOperation) { - var setOp = (AVSetOperation)previous; - var oldList = Conversion.As>(setOp.Value); - return new AVSetOperation(this.Apply(oldList, null)); - } - if (previous is AVRemoveOperation) { - var oldOp = (AVRemoveOperation)previous; - return new AVRemoveOperation(oldOp.Objects.Concat(objects)); - } - throw new InvalidOperationException("Operation is invalid after previous operation."); - } - - public object Apply(object oldValue, string key) { - if (oldValue == null) { - return new List(); - } - var oldList = Conversion.As>(oldValue); - return oldList.Except(objects, AVFieldOperations.AVObjectComparer).ToList(); - } - - public IEnumerable Objects { - get { - return objects; - } - } - } -} diff --git a/Storage/Storage/Internal_/Operation/AVSetOperation.cs b/Storage/Storage/Internal_/Operation/AVSetOperation.cs deleted file mode 100644 index 360b3fd..0000000 --- a/Storage/Storage/Internal_/Operation/AVSetOperation.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace LeanCloud.Storage.Internal { - public class AVSetOperation : IAVFieldOperation { - public AVSetOperation(object value) { - Value = value; - } - - public object Encode() { - return PointerOrLocalIdEncoder.Instance.Encode(Value); - } - - public IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous) { - return this; - } - - public object Apply(object oldValue, string key) { - return Value; - } - - public object Value { get; private set; } - } -} diff --git a/Storage/Storage/Internal_/Operation/IAVFieldOperation.cs b/Storage/Storage/Internal_/Operation/IAVFieldOperation.cs deleted file mode 100644 index 9e5bd97..0000000 --- a/Storage/Storage/Internal_/Operation/IAVFieldOperation.cs +++ /dev/null @@ -1,42 +0,0 @@ -namespace LeanCloud.Storage.Internal { - /// - /// A AVFieldOperation represents a modification to a value in a AVObject. - /// For example, setting, deleting, or incrementing a value are all different kinds of - /// AVFieldOperations. AVFieldOperations themselves can be considered to be - /// immutable. - /// - public interface IAVFieldOperation { - /// - /// Converts the AVFieldOperation to a data structure that can be converted to JSON and sent to - /// LeanCloud as part of a save operation. - /// - /// An object to be JSONified. - object Encode(); - - /// - /// Returns a field operation that is composed of a previous operation followed by - /// this operation. This will not mutate either operation. However, it may return - /// this if the current operation is not affected by previous changes. - /// For example: - /// {increment by 2}.MergeWithPrevious({set to 5}) -> {set to 7} - /// {set to 5}.MergeWithPrevious({increment by 2}) -> {set to 5} - /// {add "foo"}.MergeWithPrevious({delete}) -> {set to ["foo"]} - /// {delete}.MergeWithPrevious({add "foo"}) -> {delete} /// - /// The most recent operation on the field, or null if none. - /// A new AVFieldOperation or this. - IAVFieldOperation MergeWithPrevious(IAVFieldOperation previous); - - /// - /// Returns a new estimated value based on a previous value and this operation. This - /// value is not intended to be sent to LeanCloud, but it is used locally on the client to - /// inspect the most likely current value for a field. - /// - /// The key and object are used solely for AVRelation to be able to construct objects - /// that refer back to their parents. - /// - /// The previous value for the field. - /// The key that this value is for. - /// The new value for the field. - object Apply(object oldValue, string key); - } -} diff --git a/Storage/Storage/Internal_/Query/Controller/AVQueryController.cs b/Storage/Storage/Internal_/Query/Controller/AVQueryController.cs deleted file mode 100644 index 3eb07b9..0000000 --- a/Storage/Storage/Internal_/Query/Controller/AVQueryController.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Linq; -using System.Collections.Generic; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal { - public class AVQueryController { - public async Task> FindAsync(AVQuery query, CancellationToken cancellationToken) where T : AVObject { - IList items = await FindAsync>(query.Path, query.BuildParameters(), "results", cancellationToken); - return items.Select(item => AVObjectCoder.Instance.Decode(item as IDictionary, AVDecoder.Instance)); - } - - public async Task CountAsync(AVQuery query, CancellationToken cancellationToken) where T : AVObject { - var parameters = query.BuildParameters(); - parameters["limit"] = 0; - parameters["count"] = 1; - long ret = await FindAsync(query.Path, parameters, "count", cancellationToken); - return Convert.ToInt32(ret); - } - - public async Task FirstAsync(AVQuery query, CancellationToken cancellationToken) where T : AVObject { - var parameters = query.BuildParameters(); - parameters["limit"] = 1; - IList items = await FindAsync>(query.Path, query.BuildParameters(), "results", cancellationToken); - // Not found. Return empty state. - if (!(items.FirstOrDefault() is IDictionary item)) { - return (IObjectState)null; - } - return AVObjectCoder.Instance.Decode(item, AVDecoder.Instance); - } - - private async Task FindAsync(string path, - IDictionary parameters, - string key, - CancellationToken cancellationToken) { - var command = new AVCommand { - Path = $"{path}?{AVClient.BuildQueryString(parameters)}", - Method = HttpMethod.Get - }; - var result = await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command, cancellationToken: cancellationToken); - return (T)result.Item2[key]; - } - } -} diff --git a/Storage/Storage/Internal_/Query/IQueryCondition.cs b/Storage/Storage/Internal_/Query/IQueryCondition.cs deleted file mode 100644 index 5615ff4..0000000 --- a/Storage/Storage/Internal_/Query/IQueryCondition.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -namespace LeanCloud.Storage.Internal { - /// - /// 查询条件接口 - /// IEquatable 用于比对(替换)相同的查询条件 - /// IJsonConvertible 用于生成序列化 Dictionary - /// - public interface IQueryCondition : IEquatable, IJsonConvertible { - } -} diff --git a/Storage/Storage/Internal_/Query/QueryCompositionalCondition.cs b/Storage/Storage/Internal_/Query/QueryCompositionalCondition.cs deleted file mode 100644 index f2b87d8..0000000 --- a/Storage/Storage/Internal_/Query/QueryCompositionalCondition.cs +++ /dev/null @@ -1,270 +0,0 @@ -using System; -using System.Linq; -using System.Collections.Generic; -using System.Text.RegularExpressions; - -namespace LeanCloud.Storage.Internal { - public class QueryCombinedCondition : IQueryCondition { - public const string AND = "$and"; - public const string OR = "$or"; - - readonly List conditions; - readonly string composition; - - internal List orderBy; - internal HashSet includes; - internal HashSet selectedKeys; - internal int skip; - internal int limit; - - public QueryCombinedCondition(string composition = AND) { - conditions = new List(); - this.composition = composition; - skip = 0; - limit = 30; - } - - #region IQueryCondition - - public bool Equals(IQueryCondition other) { - return false; - } - - public IDictionary ToJSON() { - if (conditions == null || conditions.Count == 0) { - return null; - } - if (conditions.Count == 1) { - return conditions[0].ToJSON(); - } - List list = new List(); - foreach (IQueryCondition cond in conditions) { - list.Add(cond.ToJSON()); - } - return new Dictionary { - { composition, list } - }; - } - - #endregion - - #region where - - public void WhereContainedIn(string key, IEnumerable values) { - AddCondition(key, "$in", values.ToList()); - } - - public void WhereContainsAll(string key, IEnumerable values) { - AddCondition(key, "$all", values.ToList()); - } - - public void WhereContains(string key, string substring) { - AddCondition(key, "$regex", RegexQuote(substring)); - } - - public void WhereDoesNotExist(string key) { - AddCondition(key, "$exists", false); - } - - public void WhereDoesNotMatchQuery(string key, AVQuery query) where T : AVObject { - AddCondition(key, "$notInQuery", query.BuildParameters(query.ClassName)); - } - - public void WhereEndsWith(string key, string suffix) { - AddCondition(key, "$regex", RegexQuote(suffix) + "$"); - } - - public void WhereEqualTo(string key, object value) { - AddCondition(new QueryEqualCondition(key, value)); - } - - public void WhereSizeEqualTo(string key, uint size) { - AddCondition(key, "$size", size); - } - - public void WhereExists(string key) { - AddCondition(key, "$exists", true); - } - - public void WhereGreaterThan(string key, object value) { - AddCondition(key, "$gt", value); - } - - public void WhereGreaterThanOrEqualTo(string key, object value) { - AddCondition(key, "$gte", value); - } - - public void WhereLessThan(string key, object value) { - AddCondition(key, "$lt", value); - } - - public void WhereLessThanOrEqualTo(string key, object value) { - AddCondition(key, "$lte", value); - } - - public void WhereMatches(string key, Regex regex, string modifiers) { - if (!regex.Options.HasFlag(RegexOptions.ECMAScript)) { - throw new ArgumentException( - "Only ECMAScript-compatible regexes are supported. Please use the ECMAScript RegexOptions flag when creating your regex."); - } - AddCondition(key, "$regex", regex.ToString()); - AddCondition(key, "options", modifiers); - } - - public void WhereMatches(string key, Regex regex) { - WhereMatches(key, regex, null); - } - - public void WhereMatches(string key, string pattern, string modifiers) { - WhereMatches(key, new Regex(pattern, RegexOptions.ECMAScript), modifiers); - } - - public void WhereMatches(string key, string pattern) { - WhereMatches(key, pattern, null); - } - - public void WhereMatchesKeyInQuery(string key, string keyInQuery, AVQuery query) where T : AVObject { - var parameters = new Dictionary { - { "query", query.BuildParameters(query.ClassName)}, - { "key", keyInQuery} - }; - AddCondition(key, "$select", parameters); - } - - public void WhereDoesNotMatchesKeyInQuery(string key, string keyInQuery, AVQuery query) where T : AVObject { - var parameters = new Dictionary { - { "query", query.BuildParameters(query.ClassName)}, - { "key", keyInQuery} - }; - AddCondition(key, "$dontSelect", parameters); - } - - public void WhereMatchesQuery(string key, AVQuery query) where T : AVObject { - AddCondition(key, "$inQuery", query.BuildParameters(query.ClassName)); - } - - public void WhereNear(string key, AVGeoPoint point) { - AddCondition(key, "$nearSphere", point); - } - - public void WhereNotContainedIn(string key, IEnumerable values) { - AddCondition(key, "$nin", values.ToList()); - } - - public void WhereNotEqualTo(string key, object value) { - AddCondition(key, "$ne", value); - } - - public void WhereStartsWith(string key, string suffix) { - AddCondition(key, "$regex", "^" + RegexQuote(suffix)); - } - - public void WhereWithinGeoBox(string key, AVGeoPoint southwest, AVGeoPoint northeast) { - Dictionary value = new Dictionary { - { "$box", new[] { southwest, northeast } } - }; - AddCondition(key, "$within", value); - } - - public void WhereWithinDistance(string key, AVGeoPoint point, AVGeoDistance maxDistance) { - AddCondition(key, "$nearSphere", point); - AddCondition(key, "$maxDistance", maxDistance.Radians); - } - - public void WhereRelatedTo(AVObject parent, string key) { - AddCondition(new QueryRelatedCondition(parent, key)); - } - - #endregion - - public void OrderBy(string key) { - if (orderBy == null) { - orderBy = new List(); - } - orderBy.Add(key); - } - - public void OrderByDescending(string key) { - if (orderBy == null) { - orderBy = new List(); - } - orderBy.Add($"-{key}"); - } - - public void Include(string key) { - if (includes == null) { - includes = new HashSet(); - } - try { - includes.Add(key); - } catch (Exception e) { - AVClient.PrintLog(e.Message); - } - } - - public void Select(string key) { - if (selectedKeys == null) { - selectedKeys = new HashSet(); - } - try { - selectedKeys.Add(key); - } catch (Exception e) { - AVClient.PrintLog(e.Message); - } - } - - public void Skip(int count) { - skip = count; - } - - public void Limit(int count) { - limit = count; - } - - public void AddCondition(string key, string op, object value) { - QueryOperationCondition cond = new QueryOperationCondition(key, op, value); - AddCondition(cond); - } - - public void AddCondition(IQueryCondition condition) { - if (condition == null) { - return; - } - // 组合查询的 Key 为 null - conditions.RemoveAll(cond => { - return cond.Equals(condition); - }); - conditions.Add(condition); - } - - /// - /// 构建查询字符串 - /// - /// - public IDictionary BuildParameters(string className) { - Dictionary result = new Dictionary(); - if (conditions != null) { - result["where"] = ToJSON(); - } - if (orderBy != null) { - result["order"] = string.Join(",", orderBy.ToArray()); - } - if (includes != null) { - result["include"] = string.Join(",", includes.ToArray()); - } - if (selectedKeys != null) { - result["keys"] = string.Join(",", selectedKeys.ToArray()); - } - if (!string.IsNullOrEmpty(className)) { - result["className"] = className; - } - result["skip"] = skip; - result["limit"] = limit; - return result; - } - - string RegexQuote(string input) { - return "\\Q" + input.Replace("\\E", "\\E\\\\E\\Q") + "\\E"; - } - } -} diff --git a/Storage/Storage/Internal_/Query/QueryEqualCondition.cs b/Storage/Storage/Internal_/Query/QueryEqualCondition.cs deleted file mode 100644 index 9e9e152..0000000 --- a/Storage/Storage/Internal_/Query/QueryEqualCondition.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Collections.Generic; - -namespace LeanCloud.Storage.Internal { - internal class QueryEqualCondition : IQueryCondition { - readonly string key; - readonly object value; - - public QueryEqualCondition(string key, object value) { - this.key = key; - this.value = value; - } - - public bool Equals(IQueryCondition other) { - if (other is QueryEqualCondition otherCond) { - return key == otherCond.key; - } - return false; - } - - public IDictionary ToJSON() { - return new Dictionary { - { key, PointerOrLocalIdEncoder.Instance.Encode(value) } - }; - } - } -} diff --git a/Storage/Storage/Internal_/Query/QueryOperationCondition.cs b/Storage/Storage/Internal_/Query/QueryOperationCondition.cs deleted file mode 100644 index 40c2b2a..0000000 --- a/Storage/Storage/Internal_/Query/QueryOperationCondition.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Collections.Generic; - -namespace LeanCloud.Storage.Internal { - internal class QueryOperationCondition : IQueryCondition { - readonly string key; - readonly string op; - readonly object value; - - public QueryOperationCondition(string key, string op, object value) { - this.key = key; - this.op = op; - this.value = value; - } - - public bool Equals(IQueryCondition other) { - if (other is QueryOperationCondition otherCond) { - return key == otherCond.key && op == otherCond.op; - } - return false; - } - - public IDictionary ToJSON() { - return new Dictionary { - { key, new Dictionary { - { op, value } - } } - }; - } - } -} diff --git a/Storage/Storage/Internal_/Query/QueryRelatedCondition.cs b/Storage/Storage/Internal_/Query/QueryRelatedCondition.cs deleted file mode 100644 index 88e2ce3..0000000 --- a/Storage/Storage/Internal_/Query/QueryRelatedCondition.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Collections.Generic; - -namespace LeanCloud.Storage.Internal { - internal class QueryRelatedCondition : IQueryCondition { - AVObject parent; - string key; - - public QueryRelatedCondition(AVObject parent, string key) { - this.parent = parent; - this.key = key; - } - - public bool Equals(IQueryCondition other) { - if (other is QueryRelatedCondition otherCond) { - return key == otherCond.key; - } - return false; - } - - public IDictionary ToJSON() { - return new Dictionary { - { "$relatedTo", new Dictionary { - { "object", PointerOrLocalIdEncoder.Instance.Encode(parent) }, - { "key", key } - } } - }; - } - } -} diff --git a/Storage/Storage/Internal_/User/Controller/AVUserController.cs b/Storage/Storage/Internal_/User/Controller/AVUserController.cs deleted file mode 100644 index eab422f..0000000 --- a/Storage/Storage/Internal_/User/Controller/AVUserController.cs +++ /dev/null @@ -1,114 +0,0 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using System.Net.Http; - -namespace LeanCloud.Storage.Internal { - public class AVUserController { - public async Task SignUpAsync(IObjectState state, IDictionary operations) { - var objectJSON = AVObject.ToJSONObjectForSaving(operations); - var command = new AVCommand { - Path = "classes/_User", - Method = HttpMethod.Post, - Content = objectJSON - }; - var ret = await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command); - var serverState = AVObjectCoder.Instance.Decode(ret.Item2, AVDecoder.Instance); - return serverState; - } - - public async Task LogInAsync(string username, string email, string password) { - var data = new Dictionary{ - { "password", password} - }; - if (username != null) { - data.Add("username", username); - } - if (email != null) { - data.Add("email", email); - } - var command = new AVCommand { - Path = "login", - Method = HttpMethod.Post, - Content = data - }; - var ret = await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command); - var serverState = AVObjectCoder.Instance.Decode(ret.Item2, AVDecoder.Instance); - return serverState; - } - - public async Task LogInAsync(string authType, IDictionary data, bool failOnNotExist) { - var authData = new Dictionary { - [authType] = data - }; - var path = failOnNotExist ? "users?failOnNotExist=true" : "users"; - var command = new AVCommand { - Path = path, - Method = HttpMethod.Post, - Content = new Dictionary { - { "authData", authData } - } - }; - var ret = await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command); - var serverState = AVObjectCoder.Instance.Decode(ret.Item2, AVDecoder.Instance); - return serverState; - } - - public async Task GetUserAsync(string sessionToken) { - var command = new AVCommand { - Path = "users/me", - Method = HttpMethod.Get, - Headers = new Dictionary { - { "X-LC-Session", sessionToken } - } - }; - var ret = await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command); - return AVObjectCoder.Instance.Decode(ret.Item2, AVDecoder.Instance); - } - - public async Task RequestPasswordResetAsync(string email) { - var command = new AVCommand { - Path = "requestPasswordReset", - Method = HttpMethod.Post, - Content = new Dictionary { - { "email", email} - } - }; - await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command); - } - - public async Task LogInWithParametersAsync(string relativeUrl, IDictionary data) { - var command = new AVCommand { - Path = relativeUrl, - Method = HttpMethod.Post, - Content = data - }; - var ret = await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command); - var serverState = AVObjectCoder.Instance.Decode(ret.Item2, AVDecoder.Instance); - return serverState; - } - - public async Task UpdatePasswordAsync(string userId, string oldPassword, string newPassword) { - var command = new AVCommand { - Path = $"users/{userId}/updatePassword", - Method = HttpMethod.Put, - Content = new Dictionary { - { "old_password", oldPassword }, - { "new_password", newPassword }, - } - }; - var ret = await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command); - return AVObjectCoder.Instance.Decode(ret.Item2, AVDecoder.Instance); - } - - public async Task RefreshSessionTokenAsync(string userId) { - var command = new AVCommand { - Path = $"users/{userId}/refreshSessionToken", - Method = HttpMethod.Put - }; - var ret = await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command); - var serverState = AVObjectCoder.Instance.Decode(ret.Item2, AVDecoder.Instance); - return serverState; - } - } -} diff --git a/Storage/Storage/Internal_/Utilities/AVObjectExtensions.cs b/Storage/Storage/Internal_/Utilities/AVObjectExtensions.cs deleted file mode 100644 index 5ace09b..0000000 --- a/Storage/Storage/Internal_/Utilities/AVObjectExtensions.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq.Expressions; -using System.Linq; -using System.Globalization; -using System.ComponentModel; -using System.Collections; - -namespace LeanCloud.Storage.Internal -{ - /// - /// So here's the deal. We have a lot of internal APIs for AVObject, AVUser, etc. - /// - /// These cannot be 'internal' anymore if we are fully modularizing things out, because - /// they are no longer a part of the same library, especially as we create things like - /// Installation inside push library. - /// - /// So this class contains a bunch of extension methods that can live inside another - /// namespace, which 'wrap' the intenral APIs that already exist. - /// - public static class AVObjectExtensions - { - public static T FromState(IObjectState state, string defaultClassName) where T : AVObject - { - return AVObject.FromState(state, defaultClassName); - } - - public static IObjectState GetState(this AVObject obj) - { - return obj.State; - } - - public static void HandleFetchResult(this AVObject obj, IObjectState serverState) - { - obj.HandleFetchResult(serverState); - } - - public static IDictionary GetCurrentOperations(this AVObject obj) - { - return obj.operationDict; - } - - public static IDictionary Encode(this AVObject obj) - { - return PointerOrLocalIdEncoder.Instance.EncodeAVObject(obj, false); - } - - public static IDictionary ServerDataToJSONObjectForSerialization(this AVObject obj) - { - return obj.ServerDataToJSONObjectForSerialization(); - } - - public static void Set(this AVObject obj, string key, object value) - { - obj.Set(key, value); - } - - public static void DisableHooks(this AVObject obj, IEnumerable hookKeys) - { - obj.Set("__ignore_hooks", hookKeys); - } - public static void DisableHook(this AVObject obj, string hookKey) - { - var newList = new List(); - if (obj.ContainsKey("__ignore_hooks")) - { - var hookKeys = obj.Get>("__ignore_hooks"); - newList = hookKeys.ToList(); - } - newList.Add(hookKey); - obj.DisableHooks(newList); - } - - public static void DisableAfterHook(this AVObject obj) - { - obj.DisableAfterSave(); - obj.DisableAfterUpdate(); - obj.DisableAfterDelete(); - } - - public static void DisableBeforeHook(this AVObject obj) - { - obj.DisableBeforeSave(); - obj.DisableBeforeDelete(); - obj.DisableBeforeUpdate(); - } - - public static void DisableBeforeSave(this AVObject obj) - { - obj.DisableHook("beforeSave"); - } - public static void DisableAfterSave(this AVObject obj) - { - obj.DisableHook("afterSave"); - } - public static void DisableBeforeUpdate(this AVObject obj) - { - obj.DisableHook("beforeUpdate"); - } - public static void DisableAfterUpdate(this AVObject obj) - { - obj.DisableHook("afterUpdate"); - } - public static void DisableBeforeDelete(this AVObject obj) - { - obj.DisableHook("beforeDelete"); - } - public static void DisableAfterDelete(this AVObject obj) - { - obj.DisableHook("afterDelete"); - } - } -} diff --git a/Storage/Storage/Internal_/Utilities/AVRelationExtensions.cs b/Storage/Storage/Internal_/Utilities/AVRelationExtensions.cs deleted file mode 100644 index c2d2c85..0000000 --- a/Storage/Storage/Internal_/Utilities/AVRelationExtensions.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace LeanCloud.Storage.Internal { - /// - /// So here's the deal. We have a lot of internal APIs for AVObject, AVUser, etc. - /// - /// These cannot be 'internal' anymore if we are fully modularizing things out, because - /// they are no longer a part of the same library, especially as we create things like - /// Installation inside push library. - /// - /// So this class contains a bunch of extension methods that can live inside another - /// namespace, which 'wrap' the intenral APIs that already exist. - /// - public static class AVRelationExtensions { - public static AVRelation Create(AVObject parent, string childKey) where T : AVObject { - return new AVRelation(parent, childKey); - } - - public static AVRelation Create(AVObject parent, string childKey, string targetClassName) where T: AVObject { - return new AVRelation(parent, childKey, targetClassName); - } - - public static string GetTargetClassName(this AVRelation relation) where T : AVObject { - return relation.TargetClassName; - } - } -} diff --git a/Storage/Storage/Internal_/Utilities/FlexibleDictionaryWrapper.cs b/Storage/Storage/Internal_/Utilities/FlexibleDictionaryWrapper.cs deleted file mode 100644 index 5ee7d2c..0000000 --- a/Storage/Storage/Internal_/Utilities/FlexibleDictionaryWrapper.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using LeanCloud.Utilities; - -namespace LeanCloud.Storage.Internal { - /// - /// Provides a Dictionary implementation that can delegate to any other - /// dictionary, regardless of its value type. Used for coercion of - /// dictionaries when returning them to users. - /// - /// The resulting type of value in the dictionary. - /// The original type of value in the dictionary. - [Preserve(AllMembers = true, Conditional = false)] - public class FlexibleDictionaryWrapper : IDictionary { - private readonly IDictionary toWrap; - public FlexibleDictionaryWrapper(IDictionary toWrap) { - this.toWrap = toWrap; - } - - public void Add(string key, TOut value) { - toWrap.Add(key, (TIn)Conversion.ConvertTo(value)); - } - - public bool ContainsKey(string key) { - return toWrap.ContainsKey(key); - } - - public ICollection Keys { - get { return toWrap.Keys; } - } - - public bool Remove(string key) { - return toWrap.Remove(key); - } - - public bool TryGetValue(string key, out TOut value) { - TIn outValue; - bool result = toWrap.TryGetValue(key, out outValue); - value = (TOut)Conversion.ConvertTo(outValue); - return result; - } - - public ICollection Values { - get { - return toWrap.Values - .Select(item => (TOut)Conversion.ConvertTo(item)).ToList(); - } - } - - public TOut this[string key] { - get { - return (TOut)Conversion.ConvertTo(toWrap[key]); - } - set { - toWrap[key] = (TIn)Conversion.ConvertTo(value); - } - } - - public void Add(KeyValuePair item) { - toWrap.Add(new KeyValuePair(item.Key, - (TIn)Conversion.ConvertTo(item.Value))); - } - - public void Clear() { - toWrap.Clear(); - } - - public bool Contains(KeyValuePair item) { - return toWrap.Contains(new KeyValuePair(item.Key, - (TIn)Conversion.ConvertTo(item.Value))); - } - - public void CopyTo(KeyValuePair[] array, int arrayIndex) { - var converted = from pair in toWrap - select new KeyValuePair(pair.Key, - (TOut)Conversion.ConvertTo(pair.Value)); - converted.ToList().CopyTo(array, arrayIndex); - } - - public int Count { - get { return toWrap.Count; } - } - - public bool IsReadOnly { - get { return toWrap.IsReadOnly; } - } - - public bool Remove(KeyValuePair item) { - return toWrap.Remove(new KeyValuePair(item.Key, - (TIn)Conversion.ConvertTo(item.Value))); - } - - public IEnumerator> GetEnumerator() { - foreach (var pair in toWrap) { - yield return new KeyValuePair(pair.Key, - (TOut)Conversion.ConvertTo(pair.Value)); - } - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { - return this.GetEnumerator(); - } - } -} diff --git a/Storage/Storage/Internal_/Utilities/FlexibleListWrapper.cs b/Storage/Storage/Internal_/Utilities/FlexibleListWrapper.cs deleted file mode 100644 index 3db78d6..0000000 --- a/Storage/Storage/Internal_/Utilities/FlexibleListWrapper.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using LeanCloud.Utilities; - -namespace LeanCloud.Storage.Internal { - /// - /// Provides a List implementation that can delegate to any other - /// list, regardless of its value type. Used for coercion of - /// lists when returning them to users. - /// - /// The resulting type of value in the list. - /// The original type of value in the list. - [Preserve(AllMembers = true, Conditional = false)] - public class FlexibleListWrapper : IList { - private IList toWrap; - public FlexibleListWrapper(IList toWrap) { - this.toWrap = toWrap; - } - - public int IndexOf(TOut item) { - return toWrap.IndexOf((TIn)Conversion.ConvertTo(item)); - } - - public void Insert(int index, TOut item) { - toWrap.Insert(index, (TIn)Conversion.ConvertTo(item)); - } - - public void RemoveAt(int index) { - toWrap.RemoveAt(index); - } - - public TOut this[int index] { - get { - return (TOut)Conversion.ConvertTo(toWrap[index]); - } - set { - toWrap[index] = (TIn)Conversion.ConvertTo(value); - } - } - - public void Add(TOut item) { - toWrap.Add((TIn)Conversion.ConvertTo(item)); - } - - public void Clear() { - toWrap.Clear(); - } - - public bool Contains(TOut item) { - return toWrap.Contains((TIn)Conversion.ConvertTo(item)); - } - - public void CopyTo(TOut[] array, int arrayIndex) { - toWrap.Select(item => (TOut)Conversion.ConvertTo(item)) - .ToList().CopyTo(array, arrayIndex); - } - - public int Count { - get { return toWrap.Count; } - } - - public bool IsReadOnly { - get { return toWrap.IsReadOnly; } - } - - public bool Remove(TOut item) { - return toWrap.Remove((TIn)Conversion.ConvertTo(item)); - } - - public IEnumerator GetEnumerator() { - foreach (var item in (IEnumerable)toWrap) { - yield return (TOut)Conversion.ConvertTo(item); - } - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { - return this.GetEnumerator(); - } - } -} diff --git a/Storage/Storage/Internal_/Utilities/IJsonConvertible.cs b/Storage/Storage/Internal_/Utilities/IJsonConvertible.cs deleted file mode 100644 index af29ef1..0000000 --- a/Storage/Storage/Internal_/Utilities/IJsonConvertible.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace LeanCloud.Storage.Internal { - /// - /// Represents an object that can be converted into JSON. - /// - public interface IJsonConvertible { - /// - /// Converts the object to a data structure that can be converted to JSON. - /// - /// An object to be JSONified. - IDictionary ToJSON(); - } -} diff --git a/Storage/Storage/Internal_/Utilities/IdentityEqualityComparer.cs b/Storage/Storage/Internal_/Utilities/IdentityEqualityComparer.cs deleted file mode 100644 index b13f674..0000000 --- a/Storage/Storage/Internal_/Utilities/IdentityEqualityComparer.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Collections.Generic; -using System.Runtime.CompilerServices; - -namespace LeanCloud.Storage.Internal { - /// - /// An equality comparer that uses the object identity (i.e. ReferenceEquals) - /// rather than .Equals, allowing identity to be used for checking equality in - /// ISets and IDictionaries. - /// - public class IdentityEqualityComparer : IEqualityComparer - where T : AVObject { - public bool Equals(T x, T y) { - return x.ClassName == y.ClassName && - x.ObjectId == y.ObjectId; - } - - public int GetHashCode(T obj) { - return RuntimeHelpers.GetHashCode(obj); - } - } -} diff --git a/Storage/Storage/Internal_/Utilities/InternalExtensions.cs b/Storage/Storage/Internal_/Utilities/InternalExtensions.cs deleted file mode 100644 index d7a963c..0000000 --- a/Storage/Storage/Internal_/Utilities/InternalExtensions.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Runtime.ExceptionServices; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal { - /// - /// Provides helper methods that allow us to use terser code elsewhere. - /// - public static class InternalExtensions { - /// - /// Ensures a task (even null) is awaitable. - /// - /// - /// - /// - public static Task Safe(this Task task) { - return task ?? Task.FromResult(default(T)); - } - - /// - /// Ensures a task (even null) is awaitable. - /// - /// - /// - public static Task Safe(this Task task) { - return task ?? Task.FromResult(null); - } - - public delegate void PartialAccessor(ref T arg); - - public static TValue GetOrDefault(this IDictionary self, - TKey key, - TValue defaultValue) { - TValue value; - if (self.TryGetValue(key, out value)) { - return value; - } - return defaultValue; - } - - public static bool CollectionsEqual(this IEnumerable a, IEnumerable b) { - return Equals(a, b) || - (a != null && b != null && - a.SequenceEqual(b)); - } - - public static Task OnSuccess(this Task task, - Func, TResult> continuation) { - return ((Task)task).OnSuccess(t => continuation((Task)t)); - } - - public static Task OnSuccess(this Task task, Action> continuation) { - return task.OnSuccess((Func, object>)(t => { - continuation(t); - return null; - })); - } - - public static Task OnSuccess(this Task task, - Func continuation) { - return task.ContinueWith(t => { - if (t.IsFaulted) { - var ex = t.Exception.Flatten(); - if (ex.InnerExceptions.Count == 1) { - ExceptionDispatchInfo.Capture(ex.InnerExceptions[0]).Throw(); - } else { - ExceptionDispatchInfo.Capture(ex).Throw(); - } - // Unreachable - return Task.FromResult(default(TResult)); - } else if (t.IsCanceled) { - var tcs = new TaskCompletionSource(); - tcs.SetCanceled(); - return tcs.Task; - } else { - return Task.FromResult(continuation(t)); - } - }).Unwrap(); - } - - public static Task OnSuccess(this Task task, Action continuation) { - return task.OnSuccess((Func)(t => { - continuation(t); - return null; - })); - } - - public static Task WhileAsync(Func> predicate, Func body) { - Func iterate = null; - iterate = () => { - return predicate().OnSuccess(t => { - if (!t.Result) { - return Task.FromResult(0); - } - return body().OnSuccess(_ => iterate()).Unwrap(); - }).Unwrap(); - }; - return iterate(); - } - } -} diff --git a/Storage/Storage/Internal_/Utilities/Json.cs b/Storage/Storage/Internal_/Utilities/Json.cs deleted file mode 100644 index 8babc7f..0000000 --- a/Storage/Storage/Internal_/Utilities/Json.cs +++ /dev/null @@ -1,554 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal -{ - /// - /// A simple recursive-descent JSON Parser based on the grammar defined at http://www.json.org - /// and http://tools.ietf.org/html/rfc4627 - /// - public class Json - { - /// - /// Place at the start of a regex to force the match to begin wherever the search starts (i.e. - /// anchored at the index of the first character of the search, even when that search starts - /// in the middle of the string). - /// - private static readonly string startOfString = "\\G"; - private static readonly char startObject = '{'; - private static readonly char endObject = '}'; - private static readonly char startArray = '['; - private static readonly char endArray = ']'; - private static readonly char valueSeparator = ','; - private static readonly char nameSeparator = ':'; - private static readonly char[] falseValue = "false".ToCharArray(); - private static readonly char[] trueValue = "true".ToCharArray(); - private static readonly char[] nullValue = "null".ToCharArray(); - private static readonly Regex numberValue = new Regex(startOfString + - @"-?(?:0|[1-9]\d*)(?\.\d+)?(?(?:e|E)(?:-|\+)?\d+)?"); - private static readonly Regex stringValue = new Regex(startOfString + - "\"(?(?:[^\\\\\"]|(?\\\\(?:[\\\\\"/bfnrt]|u[0-9a-fA-F]{4})))*)\"", - RegexOptions.Multiline); - - private static readonly Regex escapePattern = new Regex("\\\\|\"|[\u0000-\u001F]"); - - private class JsonStringParser - { - public string Input { get; private set; } - - public char[] InputAsArray { get; private set; } - - private int currentIndex; - public int CurrentIndex - { - get - { - return currentIndex; - } - } - - public void Skip(int skip) - { - currentIndex += skip; - } - - public JsonStringParser(string input) - { - Input = input; - InputAsArray = input.ToCharArray(); - } - - /// - /// Parses JSON object syntax (e.g. '{}') - /// - internal bool AVObject(out object output) - { - output = null; - int initialCurrentIndex = CurrentIndex; - if (!Accept(startObject)) - { - return false; - } - var dict = new Dictionary(); - while (true) - { - object pairValue; - if (!ParseMember(out pairValue)) - { - break; - } - var pair = pairValue as Tuple; - dict[pair.Item1] = pair.Item2; - if (!Accept(valueSeparator)) - { - break; - } - } - if (!Accept(endObject)) - { - return false; - } - output = dict; - return true; - } - - /// - /// Parses JSON member syntax (e.g. '"keyname" : null') - /// - private bool ParseMember(out object output) - { - output = null; - object key; - if (!ParseString(out key)) - { - return false; - } - if (!Accept(nameSeparator)) - { - return false; - } - object value; - if (!ParseValue(out value)) - { - return false; - } - output = new Tuple((string)key, value); - return true; - } - - /// - /// Parses JSON array syntax (e.g. '[]') - /// - internal bool ParseArray(out object output) - { - output = null; - if (!Accept(startArray)) - { - return false; - } - var list = new List(); - while (true) - { - object value; - if (!ParseValue(out value)) - { - break; - } - list.Add(value); - if (!Accept(valueSeparator)) - { - break; - } - } - if (!Accept(endArray)) - { - return false; - } - output = list; - return true; - } - - /// - /// Parses a value (i.e. the right-hand side of an object member assignment or - /// an element in an array) - /// - private bool ParseValue(out object output) - { - if (Accept(falseValue)) - { - output = false; - return true; - } - else if (Accept(nullValue)) - { - output = null; - return true; - } - else if (Accept(trueValue)) - { - output = true; - return true; - } - return AVObject(out output) || - ParseArray(out output) || - ParseNumber(out output) || - ParseString(out output); - } - - /// - /// Parses a JSON string (e.g. '"foo\u1234bar\n"') - /// - private bool ParseString(out object output) - { - output = null; - Match m; - if (!Accept(stringValue, out m)) - { - return false; - } - // handle escapes: - int offset = 0; - var contentCapture = m.Groups["content"]; - var builder = new StringBuilder(contentCapture.Value); - foreach (Capture escape in m.Groups["escape"].Captures) - { - int index = (escape.Index - contentCapture.Index) - offset; - offset += escape.Length - 1; - builder.Remove(index + 1, escape.Length - 1); - switch (escape.Value[1]) - { - case '\"': - builder[index] = '\"'; - break; - case '\\': - builder[index] = '\\'; - break; - case '/': - builder[index] = '/'; - break; - case 'b': - builder[index] = '\b'; - break; - case 'f': - builder[index] = '\f'; - break; - case 'n': - builder[index] = '\n'; - break; - case 'r': - builder[index] = '\r'; - break; - case 't': - builder[index] = '\t'; - break; - case 'u': - builder[index] = (char)ushort.Parse(escape.Value.Substring(2), NumberStyles.AllowHexSpecifier); - break; - default: - throw new ArgumentException("Unexpected escape character in string: " + escape.Value); - } - } - output = builder.ToString(); - return true; - } - - /// - /// Parses a number. Returns a long if the number is an integer or has an exponent, - /// otherwise returns a double. - /// - private bool ParseNumber(out object output) - { - output = null; - Match m; - if (!Accept(numberValue, out m)) - { - return false; - } - if (m.Groups["frac"].Length > 0 || m.Groups["exp"].Length > 0) - { - // It's a double. - output = double.Parse(m.Value, CultureInfo.InvariantCulture); - return true; - } - else - { - int temp = 0; - if (int.TryParse(m.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out temp)) - { - output = temp; - return true; - } - output = long.Parse(m.Value, CultureInfo.InvariantCulture); - return true; - } - } - - /// - /// Matches the string to a regex, consuming part of the string and returning the match. - /// - private bool Accept(Regex matcher, out Match match) - { - match = matcher.Match(Input, CurrentIndex); - if (match.Success) - { - Skip(match.Length); - } - return match.Success; - } - - /// - /// Find the first occurrences of a character, consuming part of the string. - /// - private bool Accept(char condition) - { - int step = 0; - int strLen = InputAsArray.Length; - int currentStep = currentIndex; - char currentChar; - - // Remove whitespace - while (currentStep < strLen && - ((currentChar = InputAsArray[currentStep]) == ' ' || - currentChar == '\r' || - currentChar == '\t' || - currentChar == '\n')) - { - ++step; - ++currentStep; - } - - bool match = (currentStep < strLen) && (InputAsArray[currentStep] == condition); - if (match) - { - ++step; - ++currentStep; - - // Remove whitespace - while (currentStep < strLen && - ((currentChar = InputAsArray[currentStep]) == ' ' || - currentChar == '\r' || - currentChar == '\t' || - currentChar == '\n')) - { - ++step; - ++currentStep; - } - - Skip(step); - } - return match; - } - - /// - /// Find the first occurrences of a string, consuming part of the string. - /// - private bool Accept(char[] condition) - { - int step = 0; - int strLen = InputAsArray.Length; - int currentStep = currentIndex; - char currentChar; - - // Remove whitespace - while (currentStep < strLen && - ((currentChar = InputAsArray[currentStep]) == ' ' || - currentChar == '\r' || - currentChar == '\t' || - currentChar == '\n')) - { - ++step; - ++currentStep; - } - - bool strMatch = true; - for (int i = 0; currentStep < strLen && i < condition.Length; ++i, ++currentStep) - { - if (InputAsArray[currentStep] != condition[i]) - { - strMatch = false; - break; - } - } - - bool match = (currentStep < strLen) && strMatch; - if (match) - { - Skip(step + condition.Length); - } - return match; - } - } - - /// - /// Parses a JSON-text as defined in http://tools.ietf.org/html/rfc4627, returning an - /// IDictionary<string, object> or an IList<object> depending on whether - /// the value was an array or dictionary. Nested objects also match these types. - /// - public static object Parse(string input) - { - object output; - input = input.Trim(); - JsonStringParser parser = new JsonStringParser(input); - - if ((parser.AVObject(out output) || - parser.ParseArray(out output)) && - parser.CurrentIndex == input.Length) - { - return output; - } - throw new ArgumentException("Input JSON was invalid."); - } - - /// - /// Encodes a dictionary into a JSON string. Supports values that are - /// IDictionary<string, object>, IList<object>, strings, - /// nulls, and any of the primitive types. - /// - public static string Encode(IDictionary dict) - { - if (dict == null) - { - throw new ArgumentNullException(); - } - if (dict.Count == 0) - { - return "{}"; - } - var builder = new StringBuilder("{"); - foreach (var pair in dict) - { - builder.Append(Encode(pair.Key)); - builder.Append(":"); - builder.Append(Encode(pair.Value)); - builder.Append(","); - } - builder[builder.Length - 1] = '}'; - return builder.ToString(); - } - - /// - /// Encodes a list into a JSON string. Supports values that are - /// IDictionary<string, object>, IList<object>, strings, - /// nulls, and any of the primitive types. - /// - public static string Encode(IList list) - { - if (list == null) - { - throw new ArgumentNullException(); - } - if (list.Count == 0) - { - return "[]"; - } - var builder = new StringBuilder("["); - foreach (var item in list) - { - builder.Append(Encode(item)); - builder.Append(","); - } - builder[builder.Length - 1] = ']'; - return builder.ToString(); - } - - public static string Encode(IList strList) - { - if (strList == null) - { - throw new ArgumentNullException(); - } - if (strList.Count == 0) - { - return "[]"; - } - StringBuilder stringBuilder = new StringBuilder("["); - foreach (object obj in strList) - { - stringBuilder.Append(Json.Encode(obj)); - stringBuilder.Append(","); - } - stringBuilder[stringBuilder.Length - 1] = ']'; - return stringBuilder.ToString(); - } - - public static string Encode(IList> dicList) - { - if (dicList == null) - { - throw new ArgumentNullException(); - } - if (dicList.Count == 0) - { - return "[]"; - } - StringBuilder stringBuilder = new StringBuilder("["); - foreach (object obj in dicList) - { - stringBuilder.Append(Json.Encode(obj)); - stringBuilder.Append(","); - } - stringBuilder[stringBuilder.Length - 1] = ']'; - return stringBuilder.ToString(); - } - - /// - /// Encodes an object into a JSON string. - /// - public static string Encode(object obj) - { - var dict = obj as IDictionary; - if (dict != null) - { - return Encode(dict); - } - var list = obj as IList; - if (list != null) - { - return Encode(list); - } - var dicList = obj as IList>; - if (dicList != null) - { - return Encode(dicList); - } - var strLists = obj as IList; - if (strLists != null) - { - return Encode(strLists); - } - var str = obj as string; - if (str != null) - { - str = escapePattern.Replace(str, m => - { - switch (m.Value[0]) - { - case '\\': - return "\\\\"; - case '\"': - return "\\\""; - case '\b': - return "\\b"; - case '\f': - return "\\f"; - case '\n': - return "\\n"; - case '\r': - return "\\r"; - case '\t': - return "\\t"; - default: - return "\\u" + ((ushort)m.Value[0]).ToString("x4"); - } - }); - return "\"" + str + "\""; - } - if (obj == null) - { - return "null"; - } - if (obj is bool) - { - if ((bool)obj) - { - return "true"; - } - else - { - return "false"; - } - } - if (!obj.GetType().GetTypeInfo().IsPrimitive) - { - throw new ArgumentException("Unable to encode objects of type " + obj.GetType()); - } - return Convert.ToString(obj, CultureInfo.InvariantCulture); - } - } -} diff --git a/Storage/Storage/Internal_/Utilities/LockSet.cs b/Storage/Storage/Internal_/Utilities/LockSet.cs deleted file mode 100644 index 65e188c..0000000 --- a/Storage/Storage/Internal_/Utilities/LockSet.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal { - public class LockSet { - private static readonly ConditionalWeakTable stableIds = - new ConditionalWeakTable(); - private static long nextStableId = 0; - - private readonly IEnumerable mutexes; - - public LockSet(IEnumerable mutexes) { - this.mutexes = (from mutex in mutexes - orderby GetStableId(mutex) - select mutex).ToList(); - } - - public void Enter() { - foreach (var mutex in mutexes) { - Monitor.Enter(mutex); - } - } - - public void Exit() { - foreach (var mutex in mutexes) { - Monitor.Exit(mutex); - } - } - - private static IComparable GetStableId(object mutex) { - lock (stableIds) { - return stableIds.GetValue(mutex, k => nextStableId++); - } - } - } -} diff --git a/Storage/Storage/Internal_/Utilities/ReflectionHelpers.cs b/Storage/Storage/Internal_/Utilities/ReflectionHelpers.cs deleted file mode 100644 index 55c4833..0000000 --- a/Storage/Storage/Internal_/Utilities/ReflectionHelpers.cs +++ /dev/null @@ -1,123 +0,0 @@ -using System; -using System.Reflection; -using System.Collections.Generic; -using System.Linq; - -namespace LeanCloud.Storage.Internal -{ - public static class ReflectionHelpers - { - public static IEnumerable GetProperties(Type type) - { -#if MONO || UNITY - return type.GetProperties(); -#else - return type.GetRuntimeProperties(); -#endif - } - - public static MethodInfo GetMethod(Type type, string name, Type[] parameters) - { -#if MONO || UNITY - return type.GetMethod(name, parameters); -#else - return type.GetRuntimeMethod(name, parameters); -#endif - } - - public static bool IsPrimitive(Type type) - { -#if MONO || UNITY - return type.IsPrimitive; -#else - return type.GetTypeInfo().IsPrimitive; -#endif - } - - public static IEnumerable GetInterfaces(Type type) - { -#if MONO || UNITY - return type.GetInterfaces(); -#else - return type.GetTypeInfo().ImplementedInterfaces; -#endif - } - - public static bool IsConstructedGenericType(Type type) - { -#if UNITY - return type.IsGenericType && !type.IsGenericTypeDefinition; -#else - return type.IsConstructedGenericType; -#endif - } - - public static IEnumerable GetConstructors(Type type) - { -#if UNITY - BindingFlags searchFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; - return type.GetConstructors(searchFlags); -#else - return type.GetTypeInfo().DeclaredConstructors - .Where(c => (c.Attributes & MethodAttributes.Static) == 0); -#endif - } - - public static Type[] GetGenericTypeArguments(Type type) - { -#if UNITY - return type.GetGenericArguments(); -#else - return type.GenericTypeArguments; -#endif - } - - public static PropertyInfo GetProperty(Type type, string name) - { -#if MONO || UNITY - return type.GetProperty(name); -#else - return type.GetRuntimeProperty(name); -#endif - } - - /// - /// This method helps simplify the process of getting a constructor for a type. - /// A method like this exists in .NET but is not allowed in a Portable Class Library, - /// so we've built our own. - /// - /// - /// - /// - public static ConstructorInfo FindConstructor(this Type self, params Type[] parameterTypes) - { - var constructors = - from constructor in GetConstructors(self) - let parameters = constructor.GetParameters() - let types = from p in parameters select p.ParameterType - where types.SequenceEqual(parameterTypes) - select constructor; - return constructors.SingleOrDefault(); - } - - public static bool IsNullable(Type t) - { - bool isGeneric; -#if UNITY - isGeneric = t.IsGenericType && !t.IsGenericTypeDefinition; -#else - isGeneric = t.IsConstructedGenericType; -#endif - return isGeneric && t.GetGenericTypeDefinition().Equals(typeof(Nullable<>)); - } - - public static IEnumerable GetCustomAttributes(this Assembly assembly) where T : Attribute - { -#if UNITY - return assembly.GetCustomAttributes(typeof(T), false).Select(attr => attr as T); -#else - return CustomAttributeExtensions.GetCustomAttributes(assembly); -#endif - } - } -} diff --git a/Storage/Storage/Internal_/Utilities/SynchronizedEventHandler.cs b/Storage/Storage/Internal_/Utilities/SynchronizedEventHandler.cs deleted file mode 100644 index d94f12a..0000000 --- a/Storage/Storage/Internal_/Utilities/SynchronizedEventHandler.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal { - /// - /// Represents an event handler that calls back from the synchronization context - /// that subscribed. - /// Should look like an EventArgs, but may not inherit EventArgs if T is implemented by the Windows team. - /// - public class SynchronizedEventHandler { - private LinkedList> delegates = - new LinkedList>(); - public void Add(Delegate del) { - lock (delegates) { - TaskFactory factory; - if (SynchronizationContext.Current != null) { - factory = - new TaskFactory(CancellationToken.None, - TaskCreationOptions.None, - TaskContinuationOptions.ExecuteSynchronously, - TaskScheduler.FromCurrentSynchronizationContext()); - } else { - factory = Task.Factory; - } - foreach (var d in del.GetInvocationList()) { - delegates.AddLast(new Tuple(d, factory)); - } - } - } - - public void Remove(Delegate del) { - lock (delegates) { - if (delegates.Count == 0) { - return; - } - foreach (var d in del.GetInvocationList()) { - var node = delegates.First; - while (node != null) { - if (node.Value.Item1 == d) { - delegates.Remove(node); - break; - } - node = node.Next; - } - } - } - } - - public Task Invoke(object sender, T args) { - IEnumerable> toInvoke; - var toContinue = new[] { Task.FromResult(0) }; - lock (delegates) { - toInvoke = delegates.ToList(); - } - var invocations = toInvoke - .Select(p => p.Item2.ContinueWhenAll(toContinue, - _ => p.Item1.DynamicInvoke(sender, args))) - .ToList(); - return Task.WhenAll(invocations); - } - } -} diff --git a/Storage/Storage/Internal_/Utilities/TaskQueue.cs b/Storage/Storage/Internal_/Utilities/TaskQueue.cs deleted file mode 100644 index 4f1ee25..0000000 --- a/Storage/Storage/Internal_/Utilities/TaskQueue.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud.Storage.Internal { - /// - /// A helper class for enqueuing tasks - /// - public class TaskQueue { - /// - /// We only need to keep the tail of the queue. Cancelled tasks will - /// just complete normally/immediately when their turn arrives. - /// - private Task tail; - private readonly object mutex = new object(); - - /// - /// Gets a cancellable task that can be safely awaited and is dependent - /// on the current tail of the queue. This essentially gives us a proxy - /// for the tail end of the queue whose awaiting can be cancelled. - /// - /// A cancellation token that cancels - /// the task even if the task is still in the queue. This allows the - /// running task to return immediately without breaking the dependency - /// chain. It also ensures that errors do not propagate. - /// A new task that should be awaited by enqueued tasks. - private Task GetTaskToAwait(CancellationToken cancellationToken) { - lock (mutex) { - Task toAwait = tail ?? Task.FromResult(true); - return toAwait.ContinueWith(task => { }, cancellationToken); - } - } - - /// - /// Enqueues a task created by . If the task is - /// cancellable (or should be able to be cancelled while it is waiting in the - /// queue), pass a cancellationToken. - /// - /// The type of task. - /// A function given a task to await once state is - /// snapshotted (e.g. after capturing session tokens at the time of the save call). - /// Awaiting this task will wait for the created task's turn in the queue. - /// A cancellation token that can be used to - /// cancel waiting in the queue. - /// The task created by the taskStart function. - public T Enqueue(Func taskStart, CancellationToken cancellationToken) - where T : Task { - Task oldTail; - T task; - lock (mutex) { - oldTail = this.tail ?? Task.FromResult(true); - // The task created by taskStart is responsible for waiting the - // task passed to it before doing its work (this gives it an opportunity - // to do startup work or save state before waiting for its turn in the queue - task = taskStart(GetTaskToAwait(cancellationToken)); - - // The tail task should be dependent on the old tail as well as the newly-created - // task. This prevents cancellation of the new task from causing the queue to run - // out of order. - this.tail = Task.WhenAll(oldTail, task); - } - return task; - } - - public object Mutex { get { return mutex; } } - } -} diff --git a/Storage/Storage/Internal_/Utilities/XamarinAttributes.cs b/Storage/Storage/Internal_/Utilities/XamarinAttributes.cs deleted file mode 100644 index b4ee1c1..0000000 --- a/Storage/Storage/Internal_/Utilities/XamarinAttributes.cs +++ /dev/null @@ -1,426 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; - -namespace LeanCloud.Storage.Internal { - /// - /// A reimplementation of Xamarin's PreserveAttribute. - /// This allows us to support AOT and linking for Xamarin platforms. - /// - [AttributeUsage(AttributeTargets.All)] - internal class PreserveAttribute : Attribute { - public bool AllMembers; - public bool Conditional; - } - - [AttributeUsage(AttributeTargets.All)] - internal class LinkerSafeAttribute : Attribute { - public LinkerSafeAttribute() { } - } - - [Preserve(AllMembers = true)] - internal class PreserveWrapperTypes { - /// - /// Exists to ensure that generic types are AOT-compiled for the conversions we support. - /// Any new value types that we add support for will need to be registered here. - /// The method itself is never called, but by virtue of the Preserve attribute being set - /// on the class, these types will be AOT-compiled. - /// - /// This also applies to Unity. - /// - private static List CreateWrapperTypes() { - return new List { - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - - (Action)(() => AVCloud.CallFunctionAsync(null, null, CancellationToken.None)), - (Action)(() => AVCloud.CallFunctionAsync(null, null, CancellationToken.None)), - (Action)(() => AVCloud.CallFunctionAsync(null, null, CancellationToken.None)), - (Action)(() => AVCloud.CallFunctionAsync(null, null ,CancellationToken.None)), - (Action)(() => AVCloud.CallFunctionAsync(null, null, CancellationToken.None)), - (Action)(() => AVCloud.CallFunctionAsync(null, null, CancellationToken.None)), - (Action)(() => AVCloud.CallFunctionAsync(null, null, CancellationToken.None)), - (Action)(() => AVCloud.CallFunctionAsync(null, null, CancellationToken.None)), - (Action)(() => AVCloud.CallFunctionAsync(null, null,CancellationToken.None)), - (Action)(() => AVCloud.CallFunctionAsync(null, null, CancellationToken.None)), - (Action)(() => AVCloud.CallFunctionAsync(null, null, CancellationToken.None)), - (Action)(() => AVCloud.CallFunctionAsync(null, null, CancellationToken.None)), - (Action)(() => AVCloud.CallFunctionAsync(null, null, CancellationToken.None)), - (Action)(() => AVCloud.CallFunctionAsync>(null, null, CancellationToken.None)), - (Action)(() => AVCloud.CallFunctionAsync>(null, null, CancellationToken.None)), - - typeof(FlexibleListWrapper), - typeof(FlexibleListWrapper), - typeof(FlexibleDictionaryWrapper), - typeof(FlexibleDictionaryWrapper), - }; - } - } -} diff --git a/Storage/Storage/Public/AVACL.cs b/Storage/Storage/Public/AVACL.cs deleted file mode 100644 index 3d6d7f0..0000000 --- a/Storage/Storage/Public/AVACL.cs +++ /dev/null @@ -1,284 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using LeanCloud.Storage.Internal; - -namespace LeanCloud { - /// - /// A AVACL is used to control which users and roles can access or modify a particular object. Each - /// can have its own AVACL. You can grant read and write permissions - /// separately to specific users, to groups of users that belong to roles, or you can grant permissions - /// to "the public" so that, for example, any user could read a particular object but only a particular - /// set of users could write to that object. - /// - public class AVACL : IJsonConvertible { - private enum AccessKind { - Read, - Write - } - private const string publicName = "*"; - private readonly ICollection readers = new HashSet(); - private readonly ICollection writers = new HashSet(); - - internal AVACL(IDictionary jsonObject) { - readers = new HashSet(from pair in jsonObject - where ((IDictionary)pair.Value).ContainsKey("read") - select pair.Key); - writers = new HashSet(from pair in jsonObject - where ((IDictionary)pair.Value).ContainsKey("write") - select pair.Key); - } - - /// - /// Creates an ACL with no permissions granted. - /// - public AVACL() { - } - - /// - /// Creates an ACL where only the provided user has access. - /// - /// The only user that can read or write objects governed by this ACL. - public AVACL(AVUser owner) { - SetReadAccess(owner, true); - SetWriteAccess(owner, true); - } - - IDictionary IJsonConvertible.ToJSON() { - var result = new Dictionary(); - foreach (var user in readers.Union(writers)) { - var userPermissions = new Dictionary(); - if (readers.Contains(user)) { - userPermissions["read"] = true; - } - if (writers.Contains(user)) { - userPermissions["write"] = true; - } - result[user] = userPermissions; - } - return result; - } - - private void SetAccess(AccessKind kind, string userId, bool allowed) { - if (userId == null) { - throw new ArgumentException("Cannot set access for an unsaved user or role."); - } - ICollection target = null; - switch (kind) { - case AccessKind.Read: - target = readers; - break; - case AccessKind.Write: - target = writers; - break; - default: - throw new NotImplementedException("Unknown AccessKind"); - } - if (allowed) { - target.Add(userId); - } else { - target.Remove(userId); - } - } - - private bool GetAccess(AccessKind kind, string userId) { - if (userId == null) { - throw new ArgumentException("Cannot get access for an unsaved user or role."); - } - switch (kind) { - case AccessKind.Read: - return readers.Contains(userId); - case AccessKind.Write: - return writers.Contains(userId); - default: - throw new NotImplementedException("Unknown AccessKind"); - } - } - - /// - /// Gets or sets whether the public is allowed to read this object. - /// - public bool PublicReadAccess { - get { - return GetAccess(AccessKind.Read, publicName); - } - set { - SetAccess(AccessKind.Read, publicName, value); - } - } - - /// - /// Gets or sets whether the public is allowed to write this object. - /// - public bool PublicWriteAccess { - get { - return GetAccess(AccessKind.Write, publicName); - } - set { - SetAccess(AccessKind.Write, publicName, value); - } - } - - /// - /// Sets whether the given user id is allowed to read this object. - /// - /// The objectId of the user. - /// Whether the user has permission. - public void SetReadAccess(string userId, bool allowed) { - SetAccess(AccessKind.Read, userId, allowed); - } - - /// - /// Sets whether the given user is allowed to read this object. - /// - /// The user. - /// Whether the user has permission. - public void SetReadAccess(AVUser user, bool allowed) { - SetReadAccess(user.ObjectId, allowed); - } - - /// - /// Sets whether the given user id is allowed to write this object. - /// - /// The objectId of the user. - /// Whether the user has permission. - public void SetWriteAccess(string userId, bool allowed) { - SetAccess(AccessKind.Write, userId, allowed); - } - - /// - /// Sets whether the given user is allowed to write this object. - /// - /// The user. - /// Whether the user has permission. - public void SetWriteAccess(AVUser user, bool allowed) { - SetWriteAccess(user.ObjectId, allowed); - } - - /// - /// Gets whether the given user id is *explicitly* allowed to read this object. - /// Even if this returns false, the user may still be able to read it if - /// PublicReadAccess is true or a role that the user belongs to has read access. - /// - /// The user objectId to check. - /// Whether the user has access. - public bool GetReadAccess(string userId) { - return GetAccess(AccessKind.Read, userId); - } - - /// - /// Gets whether the given user is *explicitly* allowed to read this object. - /// Even if this returns false, the user may still be able to read it if - /// PublicReadAccess is true or a role that the user belongs to has read access. - /// - /// The user to check. - /// Whether the user has access. - public bool GetReadAccess(AVUser user) { - return GetReadAccess(user.ObjectId); - } - - /// - /// Gets whether the given user id is *explicitly* allowed to write this object. - /// Even if this returns false, the user may still be able to write it if - /// PublicReadAccess is true or a role that the user belongs to has write access. - /// - /// The user objectId to check. - /// Whether the user has access. - public bool GetWriteAccess(string userId) { - return GetAccess(AccessKind.Write, userId); - } - - /// - /// Gets whether the given user is *explicitly* allowed to write this object. - /// Even if this returns false, the user may still be able to write it if - /// PublicReadAccess is true or a role that the user belongs to has write access. - /// - /// The user to check. - /// Whether the user has access. - public bool GetWriteAccess(AVUser user) { - return GetWriteAccess(user.ObjectId); - } - - /// - /// Sets whether users belonging to the role with the given - /// are allowed to read this object. - /// - /// The name of the role. - /// Whether the role has access. - public void SetRoleReadAccess(string roleName, bool allowed) { - SetAccess(AccessKind.Read, "role:" + roleName, allowed); - } - - /// - /// Sets whether users belonging to the given role are allowed to read this object. - /// - /// The role. - /// Whether the role has access. - public void SetRoleReadAccess(AVRole role, bool allowed) { - SetRoleReadAccess(role.Name, allowed); - } - - /// - /// Gets whether users belonging to the role with the given - /// are allowed to read this object. Even if this returns false, the role may still be - /// able to read it if a parent role has read access. - /// - /// The name of the role. - /// Whether the role has access. - public bool GetRoleReadAccess(string roleName) { - return GetAccess(AccessKind.Read, "role:" + roleName); - } - - /// - /// Gets whether users belonging to the role are allowed to read this object. - /// Even if this returns false, the role may still be able to read it if a - /// parent role has read access. - /// - /// The name of the role. - /// Whether the role has access. - public bool GetRoleReadAccess(AVRole role) { - return GetRoleReadAccess(role.Name); - } - - /// - /// Sets whether users belonging to the role with the given - /// are allowed to write this object. - /// - /// The name of the role. - /// Whether the role has access. - public void SetRoleWriteAccess(string roleName, bool allowed) { - SetAccess(AccessKind.Write, "role:" + roleName, allowed); - } - - /// - /// Sets whether users belonging to the given role are allowed to write this object. - /// - /// The role. - /// Whether the role has access. - public void SetRoleWriteAccess(AVRole role, bool allowed) { - SetRoleWriteAccess(role.Name, allowed); - } - - /// - /// Gets whether users belonging to the role with the given - /// are allowed to write this object. Even if this returns false, the role may still be - /// able to write it if a parent role has write access. - /// - /// The name of the role. - /// Whether the role has access. - public bool GetRoleWriteAccess(string roleName) { - return GetAccess(AccessKind.Write, "role:" + roleName); - } - - /// - /// Gets whether users belonging to the role are allowed to write this object. - /// Even if this returns false, the role may still be able to write it if a - /// parent role has write access. - /// - /// The name of the role. - /// Whether the role has access. - public bool GetRoleWriteAccess(AVRole role) { - return GetRoleWriteAccess(role.Name); - } - } -} diff --git a/Storage/Storage/Public/AVClassNameAttribute.cs b/Storage/Storage/Public/AVClassNameAttribute.cs deleted file mode 100644 index aa82d0b..0000000 --- a/Storage/Storage/Public/AVClassNameAttribute.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LeanCloud -{ - /// - /// Defines the class name for a subclass of AVObject. - /// - [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)] - public sealed class AVClassNameAttribute : Attribute - { - /// - /// Constructs a new AVClassName attribute. - /// - /// The class name to associate with the AVObject subclass. - public AVClassNameAttribute(string className) - { - this.ClassName = className; - } - - /// - /// Gets the class name to associate with the AVObject subclass. - /// - public string ClassName { get; private set; } - } -} diff --git a/Storage/Storage/Public/AVClient.cs b/Storage/Storage/Public/AVClient.cs deleted file mode 100644 index f08ff2a..0000000 --- a/Storage/Storage/Public/AVClient.cs +++ /dev/null @@ -1,198 +0,0 @@ -using LeanCloud.Storage.Internal; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Net; -using System.Reflection; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Newtonsoft.Json; - -namespace LeanCloud { - /// - /// LeanCloud SDK 客户端类 - /// - public static class AVClient { - public static readonly string[] DateFormatStrings = { - // Official ISO format - "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'", - // It's possible that the string converter server-side may trim trailing zeroes, - // so these two formats cover ourselves from that. - "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'ff'Z'", - "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'f'Z'", - }; - - /// - /// LeanCloud SDK 配置 - /// - public class Configuration { - /// - /// App Id - /// - public string ApplicationId { get; set; } - - /// - /// App Key - /// - public string ApplicationKey { get; set; } - - /// - /// Master Key - /// - public string MasterKey { get; set; } - - /// - /// Gets or sets additional HTTP headers to be sent with network requests from the SDK. - /// - public IDictionary AdditionalHTTPHeaders { get; set; } - - /// - /// 存储服务器地址 - /// - /// The API server. - public string ApiServer { get; set; } - - /// - /// 云引擎服务器地址 - /// - /// The engine server uri. - public string EngineServer { get; set; } - - /// - /// 即时通信服务器地址 - /// - /// The RTMR outer. - public string RTMServer { get; set; } - - /// - /// 直连即时通信服务器地址 - /// - /// The realtime server. - public string RealtimeServer { get; set; } - - public Uri PushServer { get; set; } - - public Uri StatsServer { get; set; } - } - - private static readonly object mutex = new object(); - - static AVClient() { - } - - /// - /// LeanCloud SDK 当前配置 - /// - public static Configuration CurrentConfiguration { get; internal set; } - - internal static string APIVersion { - get { - return "1.1"; - } - } - - internal static string Name { - get { - return "LeanCloud-CSharp-SDK"; - } - } - - internal static string Version { - get { - return "0.1.0"; - } - } - - /// - /// 初始化 LeanCloud SDK - /// - /// App Id - /// App Key - public static void Initialize(string applicationId, string applicationKey) { - Initialize(new Configuration { - ApplicationId = applicationId, - ApplicationKey = applicationKey - }); - } - - public static void Initialize(string applicationId, string applicationKey, string serverUrl) { - Initialize(new Configuration { - ApplicationId = applicationId, - ApplicationKey = applicationKey, - ApiServer = serverUrl, - EngineServer = serverUrl, - RTMServer = serverUrl - }); - } - - internal static Action LogTracker { get; private set; } - - /// - /// 启动日志打印 - /// - /// - public static void HttpLog(Action trace) { - LogTracker = trace; - } - /// - /// 打印 HTTP 访问日志 - /// - /// - public static void PrintLog(string log) { - LogTracker?.Invoke(log); - } - - /// - /// 是否使用生产环境 - /// - public static bool UseProduction { - get; set; - } - - /// - /// 是否使用 MasterKey - /// - public static bool UseMasterKey { - get; set; - } - - /// - /// 初始化 LeanCloud - /// - /// 初始化配置 - public static void Initialize(Configuration configuration) { - CurrentConfiguration = configuration; - - AVObject.RegisterSubclass(); - AVObject.RegisterSubclass(); - AVObject.RegisterSubclass(); - } - - internal static void Clear() { - AVPlugins.Instance.AppRouterController.Clear(); - AVPlugins.Instance.Reset(); - } - - public static string BuildQueryString(IDictionary parameters) { - return string.Join("&", (from pair in parameters - let valueString = pair.Value as string - select string.Format("{0}={1}", - Uri.EscapeDataString(pair.Key), - Uri.EscapeDataString(string.IsNullOrEmpty(valueString) ? - JsonConvert.SerializeObject(pair.Value) : valueString))) - .ToArray()); - } - - internal static IDictionary DecodeQueryString(string queryString) { - var dict = new Dictionary(); - foreach (var pair in queryString.Split('&')) { - var parts = pair.Split(new char[] { '=' }, 2); - dict[parts[0]] = parts.Length == 2 ? Uri.UnescapeDataString(parts[1].Replace("+", " ")) : null; - } - return dict; - } - } -} diff --git a/Storage/Storage/Public/AVCloud.cs b/Storage/Storage/Public/AVCloud.cs deleted file mode 100644 index 6c6cf3a..0000000 --- a/Storage/Storage/Public/AVCloud.cs +++ /dev/null @@ -1,311 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using LeanCloud.Storage.Internal; -using System.Net.Http; - -namespace LeanCloud { - /// - /// AVCloud,提供与 LeanCloud 云函数交互的接口 - /// - public static class AVCloud { - internal static AVCloudCodeController CloudCodeController { - get { - return AVPlugins.Instance.CloudCodeController; - } - } - - /// - /// Calls a cloud function. - /// - /// The type of data you will receive from the cloud function. This - /// can be an IDictionary, string, IList, AVObject, or any other type supported by - /// AVObject. - /// The cloud function to call. - /// The parameters to send to the cloud function. This - /// dictionary can contain anything that could be passed into a AVObject except for - /// AVObjects themselves. - /// The cancellation token. - /// The result of the cloud call. - public static Task CallFunctionAsync(string name, IDictionary parameters = null, CancellationToken cancellationToken = default) { - return CloudCodeController.CallFunctionAsync(name, parameters, cancellationToken); - } - - /// - /// 远程调用云函数,返回结果会反序列化为 . - /// - /// - /// - /// - /// - /// - public static Task RPCFunctionAsync(string name, IDictionary parameters = null, CancellationToken cancellationToken = default) { - return CloudCodeController.RPCFunction(name, parameters, cancellationToken); - } - - /// - /// 请求发送验证码。 - /// - /// 是否发送成功。 - /// 手机号。 - /// 应用名称。 - /// 进行的操作名称。 - /// 验证码失效时间。 - /// Cancellation token。 - public static Task RequestSMSCodeAsync(string mobilePhoneNumber, string name, string op, int ttl = 10, CancellationToken cancellationToken = default) { - if (string.IsNullOrEmpty(mobilePhoneNumber)) { - throw new AVException(AVException.ErrorCode.MobilePhoneInvalid, "Moblie Phone number is invalid.", null); - } - - Dictionary strs = new Dictionary() - { - { "mobilePhoneNumber", mobilePhoneNumber }, - }; - if (!string.IsNullOrEmpty(name)) { - strs.Add("name", name); - } - if (!string.IsNullOrEmpty(op)) { - strs.Add("op", op); - } - if (ttl > 0) { - strs.Add("TTL", ttl); - } - var command = new EngineCommand { - Path = "requestSmsCode", - Method = HttpMethod.Post, - Content = strs - }; - return AVPlugins.Instance.CommandRunner.RunCommandAsync>(command, cancellationToken); - } - - /// - /// 请求发送验证码。 - /// - /// 是否发送成功。 - /// 手机号。 - public static Task RequestSMSCodeAsync(string mobilePhoneNumber) { - return RequestSMSCodeAsync(mobilePhoneNumber, CancellationToken.None); - } - - - /// - /// 请求发送验证码。 - /// - /// 是否发送成功。 - /// 手机号。 - /// Cancellation Token. - public static Task RequestSMSCodeAsync(string mobilePhoneNumber, CancellationToken cancellationToken) { - return RequestSMSCodeAsync(mobilePhoneNumber, null, null, 0, cancellationToken); - } - - /// - /// 发送手机短信,并指定模板以及传入模板所需的参数。 - /// - /// - /// - /// - /// - /// - /// - public static Task RequestSMSCodeAsync( - string mobilePhoneNumber, - string template, - IDictionary env, - string sign = "", - string validateToken = "") { - - if (string.IsNullOrEmpty(mobilePhoneNumber)) { - throw new AVException(AVException.ErrorCode.MobilePhoneInvalid, "Moblie Phone number is invalid.", null); - } - Dictionary strs = new Dictionary() - { - { "mobilePhoneNumber", mobilePhoneNumber }, - }; - strs.Add("template", template); - if (string.IsNullOrEmpty(sign)) { - strs.Add("sign", sign); - } - if (string.IsNullOrEmpty(validateToken)) { - strs.Add("validate_token", validateToken); - } - foreach (var key in env.Keys) { - strs.Add(key, env[key]); - } - var command = new EngineCommand { - Path = "requestSmsCode", - Method = HttpMethod.Post, - Content = strs - }; - return AVPlugins.Instance.CommandRunner.RunCommandAsync>(command); - } - - /// - /// - /// - /// - /// - public static Task RequestVoiceCodeAsync(string mobilePhoneNumber) { - if (string.IsNullOrEmpty(mobilePhoneNumber)) { - throw new AVException(AVException.ErrorCode.MobilePhoneInvalid, "Moblie Phone number is invalid.", null); - } - Dictionary body = new Dictionary() - { - { "mobilePhoneNumber", mobilePhoneNumber }, - { "smsType", "voice" }, - { "IDD", "+86" } - }; - - var command = new EngineCommand { - Path = "requestSmsCode", - Method = HttpMethod.Post, - Content = body - }; - - return AVPlugins.Instance.CommandRunner.RunCommandAsync>(command); - } - - /// - /// 验证是否是有效短信验证码。 - /// - /// 是否验证通过。 - /// 手机号 - /// 验证码。 - public static Task VerifySmsCodeAsync(string code, string mobilePhoneNumber) { - return VerifySmsCodeAsync(code, mobilePhoneNumber, CancellationToken.None); - } - - /// - /// 验证是否是有效短信验证码。 - /// - /// 是否验证通过。 - /// 验证码。 - /// 手机号 - /// Cancellation token. - public static Task VerifySmsCodeAsync(string code, string mobilePhoneNumber, CancellationToken cancellationToken) { - var command = new AVCommand { - Path = $"verifySmsCode/{code.Trim()}?mobilePhoneNumber={mobilePhoneNumber.Trim()}", - }; - return AVPlugins.Instance.CommandRunner.RunCommandAsync>(command, cancellationToken); - } - - /// - /// Stands for a captcha result. - /// - public class Captcha { - /// - /// Used for captcha verify. - /// - public string Token { internal set; get; } - - /// - /// Captcha image URL. - /// - public string Url { internal set; get; } - - /// - /// Verify the user's input of catpcha. - /// - /// User's input of this captcha. - /// - public Task VerifyAsync(string code) { - return AVCloud.VerifyCaptchaAsync(code, Token); - } - } - - /// - /// Get a captcha image. - /// - /// captcha image width. - /// captcha image height. - /// CancellationToken. - /// an instance of Captcha. - public static async Task RequestCaptchaAsync(int width = 85, int height = 30, CancellationToken cancellationToken = default) { - var path = string.Format("requestCaptcha?width={0}&height={1}", width, height); - var command = new AVCommand { - Path = $"requestCaptcha?width={width}&height={height}", - Method = HttpMethod.Get - }; - var result = await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command, cancellationToken); - var decoded = AVDecoder.Instance.Decode(result.Item2) as IDictionary; - return new Captcha { - Token = decoded["captcha_token"] as string, - Url = decoded["captcha_url"] as string, - }; - } - - /// - /// Verify the user's input of catpcha. - /// - /// The captcha's token, from server. - /// User's input of this captcha. - /// CancellationToken. - /// - public static async Task VerifyCaptchaAsync(string code, string token, CancellationToken cancellationToken = default) { - var data = new Dictionary { - { "captcha_token", token }, - { "captcha_code", code }, - }; - var command = new AVCommand { - Path = "verifyCaptcha", - Method = HttpMethod.Post, - Content = data - }; - var result = await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command, cancellationToken); - if (result.Item2.TryGetValue("validate_token", out object tokenObj)) { - return tokenObj as string; - } - throw new KeyNotFoundException("validate_token"); - } - - /// - /// Get the custom cloud parameters, you can set them at console https://leancloud.cn/dashboard/devcomponent.html?appid={your_app_Id}#/component/custom_param - /// - /// - /// - public static async Task> GetCustomParametersAsync(CancellationToken cancellationToken = default) { - var command = new AVCommand { - Path = $"statistics/apps/{AVClient.CurrentConfiguration.ApplicationId}/sendPolicy", - Method = HttpMethod.Get - }; - var data = await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command, cancellationToken); - var settings = data.Item2; - var CloudParameters = settings["parameters"] as IDictionary; - return CloudParameters; - } - - public class RealtimeSignature { - public string Nonce { internal set; get; } - public long Timestamp { internal set; get; } - public string ClientId { internal set; get; } - public string Signature { internal set; get; } - } - - public static async Task RequestRealtimeSignatureAsync(CancellationToken cancellationToken = default) { - var command = new AVCommand { - Path = "rtm/sign", - Method = HttpMethod.Post, - Content = new Dictionary { - { "session_token", AVUser.CurrentUser?.SessionToken } - } - }; - var result = await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command, cancellationToken); - var body = result.Item2; - return new RealtimeSignature() { - Nonce = body["nonce"] as string, - Timestamp = (long)body["timestamp"], - ClientId = body["client_id"] as string, - Signature = body["signature"] as string, - }; - } - - /// - /// Gets the LeanEngine hosting URL for current app async. - /// - /// The lean engine hosting URL async. - public static Task GetLeanEngineHostingUrlAsync() { - return CallFunctionAsync("_internal_extensions_get_domain"); - } - - } -} diff --git a/Storage/Storage/Public/AVException.cs b/Storage/Storage/Public/AVException.cs deleted file mode 100644 index 125363f..0000000 --- a/Storage/Storage/Public/AVException.cs +++ /dev/null @@ -1,285 +0,0 @@ -using System; - -namespace LeanCloud -{ - /// - /// Exceptions that may occur when sending requests to LeanCloud. - /// - public class AVException : Exception - { - /// - /// Error codes that may be delivered in response to requests to LeanCloud. - /// - public enum ErrorCode - { - /// - /// Error code indicating that an unknown error or an error unrelated to LeanCloud - /// occurred. - /// - OtherCause = -1, - - /// - /// Error code indicating that something has gone wrong with the server. - /// If you get this error code, it is LeanCloud's fault. - /// - InternalServerError = 1, - - /// - /// Error code indicating the connection to the LeanCloud servers failed. - /// - ConnectionFailed = 100, - - /// - /// Error code indicating the specified object doesn't exist. - /// - ObjectNotFound = 101, - - /// - /// Error code indicating you tried to query with a datatype that doesn't - /// support it, like exact matching an array or object. - /// - InvalidQuery = 102, - - /// - /// Error code indicating a missing or invalid classname. Classnames are - /// case-sensitive. They must start with a letter, and a-zA-Z0-9_ are the - /// only valid characters. - /// - InvalidClassName = 103, - - /// - /// Error code indicating an unspecified object id. - /// - MissingObjectId = 104, - - /// - /// Error code indicating an invalid key name. Keys are case-sensitive. They - /// must start with a letter, and a-zA-Z0-9_ are the only valid characters. - /// - InvalidKeyName = 105, - - /// - /// Error code indicating a malformed pointer. You should not see this unless - /// you have been mucking about changing internal LeanCloud code. - /// - InvalidPointer = 106, - - /// - /// Error code indicating that badly formed JSON was received upstream. This - /// either indicates you have done something unusual with modifying how - /// things encode to JSON, or the network is failing badly. - /// - InvalidJSON = 107, - - /// - /// Error code indicating that the feature you tried to access is only - /// available internally for testing purposes. - /// - CommandUnavailable = 108, - - /// - /// You must call LeanCloud.initialize before using the LeanCloud library. - /// - NotInitialized = 109, - - /// - /// Error code indicating that a field was set to an inconsistent type. - /// - IncorrectType = 111, - - /// - /// Error code indicating an invalid channel name. A channel name is either - /// an empty string (the broadcast channel) or contains only a-zA-Z0-9_ - /// characters and starts with a letter. - /// - InvalidChannelName = 112, - - /// - /// Error code indicating that push is misconfigured. - /// - PushMisconfigured = 115, - - /// - /// Error code indicating that the object is too large. - /// - ObjectTooLarge = 116, - - /// - /// Error code indicating that the operation isn't allowed for clients. - /// - OperationForbidden = 119, - - /// - /// Error code indicating the result was not found in the cache. - /// - CacheMiss = 120, - - /// - /// Error code indicating that an invalid key was used in a nested - /// JSONObject. - /// - InvalidNestedKey = 121, - - /// - /// Error code indicating that an invalid filename was used for AVFile. - /// A valid file name contains only a-zA-Z0-9_. characters and is between 1 - /// and 128 characters. - /// - InvalidFileName = 122, - - /// - /// Error code indicating an invalid ACL was provided. - /// - InvalidACL = 123, - - /// - /// Error code indicating that the request timed out on the server. Typically - /// this indicates that the request is too expensive to run. - /// - Timeout = 124, - - /// - /// Error code indicating that the email address was invalid. - /// - InvalidEmailAddress = 125, - - /// - /// Error code indicating that a unique field was given a value that is - /// already taken. - /// - DuplicateValue = 137, - - /// - /// Error code indicating that a role's name is invalid. - /// - InvalidRoleName = 139, - - /// - /// Error code indicating that an application quota was exceeded. Upgrade to - /// resolve. - /// - ExceededQuota = 140, - - /// - /// Error code indicating that a Cloud Code script failed. - /// - ScriptFailed = 141, - - /// - /// Error code indicating that a Cloud Code validation failed. - /// - ValidationFailed = 142, - - /// - /// Error code indicating that deleting a file failed. - /// - FileDeleteFailed = 153, - - /// - /// Error code indicating that the application has exceeded its request limit. - /// - RequestLimitExceeded = 155, - - /// - /// Error code indicating that the provided event name is invalid. - /// - InvalidEventName = 160, - - /// - /// Error code indicating that the username is missing or empty. - /// - UsernameMissing = 200, - - /// - /// Error code indicating that the password is missing or empty. - /// - PasswordMissing = 201, - - /// - /// Error code indicating that the username has already been taken. - /// - UsernameTaken = 202, - - /// - /// Error code indicating that the email has already been taken. - /// - EmailTaken = 203, - - /// - /// Error code indicating that the email is missing, but must be specified. - /// - EmailMissing = 204, - - /// - /// Error code indicating that a user with the specified email was not found. - /// - EmailNotFound = 205, - - /// - /// Error code indicating that a user object without a valid session could - /// not be altered. - /// - SessionMissing = 206, - - /// - /// Error code indicating that a user can only be created through signup. - /// - MustCreateUserThroughSignup = 207, - - /// - /// Error code indicating that an an account being linked is already linked - /// to another user. - /// - AccountAlreadyLinked = 208, - - /// - /// Error code indicating that the current session token is invalid. - /// - InvalidSessionToken = 209, - - /// - /// Error code indicating that a user cannot be linked to an account because - /// that account's id could not be found. - /// - LinkedIdMissing = 250, - - /// - /// Error code indicating that a user with a linked (e.g. Facebook) account - /// has an invalid session. - /// - InvalidLinkedSession = 251, - - /// - /// Error code indicating that a service being linked (e.g. Facebook or - /// Twitter) is unsupported. - /// - UnsupportedService = 252, - - /// - /// 手机号不合法 - /// - MobilePhoneInvalid = 253, - - /// - /// 按条件更新/删除失败 - /// - NoEffectOnUpdatingOrDeleting = 305, - - /// - /// 循环引用 - /// - CircleReference = 400, - } - - internal AVException(ErrorCode code, string message, Exception cause = null) - : base(message, cause) - { - this.Code = code; - } - - /// - /// The LeanCloud error code associated with the exception. - /// - public ErrorCode Code { get; private set; } - } -} diff --git a/Storage/Storage/Public/AVExtensions.cs b/Storage/Storage/Public/AVExtensions.cs deleted file mode 100644 index 5f00067..0000000 --- a/Storage/Storage/Public/AVExtensions.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using System.Linq; -using LeanCloud.Storage.Internal; - -namespace LeanCloud { - /// - /// Provides convenience extension methods for working with collections - /// of AVObjects so that you can easily save and fetch them in batches. - /// - public static class AVExtensions { - /// - /// Saves all of the AVObjects in the enumeration. Equivalent to - /// calling - /// . - /// - /// The objects to save. - /// The cancellation token. - public static Task SaveAllAsync(this IEnumerable objects, CancellationToken cancellationToken = default) - where T : AVObject { - return AVObject.SaveAllAsync(objects, cancellationToken); - } - - /// - /// Fetches all of the objects in the enumeration. Equivalent to - /// calling . - /// - /// The objects to save. - public static Task> FetchAllAsync(this IEnumerable objects) - where T : AVObject { - return AVObject.FetchAllAsync(objects); - } - - /// - /// Fetches all of the objects in the enumeration. Equivalent to - /// calling - /// . - /// - /// The objects to fetch. - /// The cancellation token. - public static Task> FetchAllAsync( - this IEnumerable objects, CancellationToken cancellationToken) - where T : AVObject { - return AVObject.FetchAllAsync(objects, cancellationToken); - } - - /// - /// Fetches all of the objects in the enumeration that don't already have - /// data. Equivalent to calling - /// . - /// - /// The objects to fetch. - public static Task> FetchAllIfNeededAsync( - this IEnumerable objects) - where T : AVObject { - return AVObject.FetchAllIfNeededAsync(objects); - } - - /// - /// Fetches all of the objects in the enumeration that don't already have - /// data. Equivalent to calling - /// . - /// - /// The objects to fetch. - /// The cancellation token. - public static Task> FetchAllIfNeededAsync( - this IEnumerable objects, CancellationToken cancellationToken) - where T : AVObject { - return AVObject.FetchAllIfNeededAsync(objects, cancellationToken); - } - - /// - /// Constructs a query that is the or of the given queries. - /// - /// The type of AVObject being queried. - /// An initial query to 'or' with additional queries. - /// The list of AVQueries to 'or' together. - /// A query that is the or of the given queries. - public static AVQuery Or(this AVQuery source, params AVQuery[] queries) - where T : AVObject { - return AVQuery.Or(queries.Concat(new[] { source })); - } - - public static Task FetchAsync(this T obj, - IEnumerable keys = null, IEnumerable includes = null, AVACL includeACL = null, - CancellationToken cancellationToken = default) where T : AVObject { - var queryString = new Dictionary(); - if (keys != null) { - var encode = string.Join(",", keys.ToArray()); - queryString.Add("keys", encode); - } - if (includes != null) { - var encode = string.Join(",", includes.ToArray()); - queryString.Add("include", encode); - } - if (includeACL != null) { - queryString.Add("returnACL", includeACL); - } - return obj.FetchAsyncInternal(queryString, cancellationToken).OnSuccess(t => (T)t.Result); - } - } -} diff --git a/Storage/Storage/Public/AVFieldNameAttribute.cs b/Storage/Storage/Public/AVFieldNameAttribute.cs deleted file mode 100644 index 7abfd2b..0000000 --- a/Storage/Storage/Public/AVFieldNameAttribute.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; - -namespace LeanCloud { - /// - /// Specifies a field name for a property on a AVObject subclass. - /// - [AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)] - public sealed class AVFieldNameAttribute : Attribute { - /// - /// Constructs a new AVFieldName attribute. - /// - /// The name of the field on the AVObject that the - /// property represents. - public AVFieldNameAttribute(string fieldName) { - FieldName = fieldName; - } - - /// - /// Gets the name of the field represented by this property. - /// - public string FieldName { get; private set; } - } -} diff --git a/Storage/Storage/Public/AVFile.cs b/Storage/Storage/Public/AVFile.cs deleted file mode 100644 index b5026fa..0000000 --- a/Storage/Storage/Public/AVFile.cs +++ /dev/null @@ -1,565 +0,0 @@ -using LeanCloud.Storage.Internal; -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud { - /// - /// AVFile 存储于 LeanCloud 的文件类 - /// - [AVClassName("_File")] - public class AVFile : AVObject { - const string QCloud = "qcloud"; - const string AWS = "s3"; - - #region Properties - - /// - /// Gets the name of the file. Before save is called, this is the filename given by - /// the user. After save is called, that name gets prefixed with a unique identifier. - /// - [AVFieldName("name")] - public string Name { - get { - return GetProperty("Name"); - } - set { - SetProperty(value, "Name"); - } - } - - [AVFieldName("key")] - public string Key { - get { - return GetProperty("Key"); - } - set { - SetProperty(value, "Key"); - } - } - - /// - /// Gets the MIME type of the file. This is either passed in to the constructor or - /// inferred from the file extension. "unknown/unknown" will be used if neither is - /// available. - /// - [AVFieldName("mime_type")] - public string MimeType { - get { - return GetProperty("MimeType"); - } - set { - SetProperty(value, "MimeType"); - } - } - - /// - /// Gets the url of the file. It is only available after you save the file or after - /// you get the file from a . - /// - [AVFieldName("url")] - public string Url { - get { - return GetProperty("Url"); - } - set { - SetProperty(value, "Url"); - } - } - - /// - /// 文件的元数据。 - /// - [AVFieldName("metaData")] - public IDictionary MetaData { - get { - return GetProperty>("MetaData"); - } - set { - SetProperty(value, "MetaData"); - } - } - - #endregion - - private readonly Stream dataStream; - - #region Constructor - /// - /// 通过文件名,数据流,文件类型,文件源信息构建一个 AVFile - /// - /// 文件名 - /// 数据流 - /// 文件类型 - /// 文件源信息 - public AVFile(string name, Stream data, string mimeType = null, IDictionary metaData = null) { - Name = name; - MimeType = mimeType ?? GetMIMEType(name); - MetaData = metaData; - dataStream = data; - } - - /// - /// 根据文件名,文件 Byte 数组,以及文件类型构建 AVFile - /// - /// 文件名 - /// 文件 Byte 数组 - /// 文件类型 - public AVFile(string name, byte[] data, string mimeType = null) - : this(name, new MemoryStream(data), mimeType) { } - - /// - /// 根据文件名,文件流数据,文件类型构建 AVFile - /// - /// 文件名 - /// 文件流数据 - /// 文件类型 - public AVFile(string name, Stream data, string mimeType = null) - : this(name, data, mimeType, new Dictionary()) { - } - - /// - /// 根据 byte 数组以及文件名创建文件 - /// - /// 文件名 - /// 文件的 byte[] 数据 - public AVFile(string name, byte[] data) - : this(name, new MemoryStream(data), new Dictionary()) { - - } - - /// - /// 根据文件名,数据 byte[] 数组以及元数据创建文件 - /// - /// 文件名 - /// 文件的 byte[] 数据 - /// 元数据 - public AVFile(string name, byte[] data, IDictionary metaData) - : this(name, new MemoryStream(data), metaData) { - - } - - /// - /// 根据文件名,数据流以及元数据创建文件 - /// - /// 文件名 - /// 文件的数据流 - /// 元数据 - public AVFile(string name, Stream data, IDictionary metaData) - : this(name, data, GetMIMEType(name), metaData) { - } - - /// - /// 根据文件名,数据流以及元数据创建文件 - /// - /// 文件名 - /// 文件的数据流 - public AVFile(string name, Stream data) - : this(name, data, new Dictionary()) { - - } - - public AVFile() { - - } - - #endregion - - #region created by url or uri - - /// - /// 根据文件名,Uri,文件类型以及文件源信息 - /// - /// 文件名 - /// 文件Uri - /// 文件类型 - /// 文件源信息 - public AVFile(string name, Uri uri, string mimeType = null, IDictionary metaData = null) { - Name = name; - MimeType = mimeType ?? GetMIMEType(name); - IsExternal = true; - } - - /// - /// 根据文件名,文件 Url,文件类型,文件源信息构建 AVFile - /// - /// 文件名 - /// 文件 Url - /// 文件类型 - /// 文件源信息 - public AVFile(string name, string url, string mimeType = null, IDictionary metaData = null) - : this(name, new Uri(url), mimeType, metaData) { - - } - - /// - /// 根据文件名,文件 Url以及文件的源信息构建 AVFile - /// - /// 文件名 - /// 文件 Url - /// 文件源信息 - public AVFile(string name, string url, IDictionary metaData) - : this(name, url, null, metaData) { - } - - /// - /// 根据文件名,文件 Uri,以及文件类型构建 AVFile - /// - /// 文件名 - /// 文件 Uri - /// 文件类型 - public AVFile(string name, Uri uri, string mimeType = null) - : this(name, uri, mimeType, new Dictionary()) { - - } - - /// - /// 根据文件名以及文件 Uri 构建 AVFile - /// - /// 文件名 - /// 文件 Uri - public AVFile(string name, Uri uri) - : this(name, uri, null, new Dictionary()) { - - } - /// - /// 根据文件名和 Url 创建文件 - /// - /// 文件名 - /// 文件的 Url - public AVFile(string name, string url) { - Name = name; - Url = url; - IsExternal = true; - } - - #endregion - - - - /// - /// 获取缩略图 - /// - /// 宽 - /// 高 - /// 质量 1-100 - /// 等比缩放,是否裁剪 - /// 格式 - /// - public string GetThumbnailUrl(int width, int height, - int quality = 100, bool scaleToFit = true, string format = "png") { - int mode = scaleToFit ? 2 : 1; - return $"{Url}?imageView/{mode}/w/{width}/h/{height}/q/{quality}/format/{format}"; - } - - internal static AVFileController FileController { - get { - return AVPlugins.Instance.FileController; - } - } - - IDictionary ToJSON() { - if (IsDirty) { - throw new InvalidOperationException( - "AVFile must be saved before it can be serialized."); - } - return new Dictionary { - { "__type", "File"} , - { "id", ObjectId }, - { "name", Name }, - { "url", Url } - }; - } - - #region Save - - /// - /// 保存文件 - /// - /// The cancellation token. - public override async Task SaveAsync(bool fetchWhenSave = false, AVQuery query = null, CancellationToken cancellationToken = default) { - if (IsExternal) { - await base.SaveAsync(fetchWhenSave, query, cancellationToken); - } else { - // 需不需要判断数据的 dirty? - // 获取 FileToken 并创建对应记录 - Tuple> res = await AVPlugins.Instance.FileController.GetFileToken(Name, MetaData); - IObjectState serverState = AVObjectCoder.Instance.Decode(res.Item2, AVDecoder.Instance); - HandleSave(serverState); - IDictionary fileData = res.Item2; - AVClient.PrintLog(fileData.ToString()); - - var provider = fileData["provider"] as string; - switch (provider) { - case QCloud: { - await new QCloudUploader { - FileName = Key, - UploadUrl = fileData["upload_url"] as string, - Token = fileData["token"] as string, - Bucket = fileData["bucket"] as string, - Stream = dataStream - }.Upload(); - } - break; - case AWS: { - await new AWSUploader { - UploadUrl = fileData["upload_url"] as string, - MimeType = MimeType, - Stream = dataStream - }.Upload(); - } - break; - default: { - await new QiniuUploader { - Key = Key, - Token = fileData["token"] as string, - MimeType = MimeType, - MetaData = MetaData, - Stream = dataStream - }.Upload(); - } - break; - } - } - } - - #endregion - - #region Compatible - - /// - /// 文件是否为外链文件。 - /// - /// - /// - public bool IsExternal { - get; private set; - } - - internal static string GetUniqueName(string name) { - string key = Guid.NewGuid().ToString(); - string extension = Path.GetExtension(name); - key += extension; - return key; - } - - internal static string GetMIMEType(string fileName) { - try { - string str = Path.GetExtension(fileName).Remove(0, 1); - if (!MIMETypesDictionary.ContainsKey(str)) { - return "unknown/unknown"; - } - return MIMETypesDictionary[str]; - } catch { - return "unknown/unknown"; - } - } - - //public static AVFile CreateWithData(string objectId, string name, string url, IDictionary metaData) { - // var fileState = new FileState { - // Name = name, - // ObjectId = objectId, - // Url = new Uri(url), - // MetaData = metaData - // }; - // return CreateWithState(fileState); - //} - - #endregion - - private readonly static Dictionary MIMETypesDictionary = new Dictionary { - { "ai", "application/postscript" }, - { "aif", "audio/x-aiff" }, - { "aifc", "audio/x-aiff" }, - { "aiff", "audio/x-aiff" }, - { "asc", "text/plain" }, - { "atom", "application/atom+xml" }, - { "au", "audio/basic" }, - { "avi", "video/x-msvideo" }, - { "bcpio", "application/x-bcpio" }, - { "bin", "application/octet-stream" }, - { "bmp", "image/bmp" }, - { "cdf", "application/x-netcdf" }, - { "cgm", "image/cgm" }, - { "class", "application/octet-stream" }, - { "cpio", "application/x-cpio" }, - { "cpt", "application/mac-compactpro" }, - { "csh", "application/x-csh" }, - { "css", "text/css" }, - { "dcr", "application/x-director" }, - { "dif", "video/x-dv" }, - { "dir", "application/x-director" }, - { "djv", "image/vnd.djvu" }, - { "djvu", "image/vnd.djvu" }, - { "dll", "application/octet-stream" }, - { "dmg", "application/octet-stream" }, - { "dms", "application/octet-stream" }, - { "doc", "application/msword" }, - { "docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }, - { "dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template" }, - { "docm", "application/vnd.ms-word.document.macroEnabled.12" }, - { "dotm", "application/vnd.ms-word.template.macroEnabled.12" }, - { "dtd", "application/xml-dtd" }, - { "dv", "video/x-dv" }, - { "dvi", "application/x-dvi" }, - { "dxr", "application/x-director" }, - { "eps", "application/postscript" }, - { "etx", "text/x-setext" }, - { "exe", "application/octet-stream" }, - { "ez", "application/andrew-inset" }, - { "gif", "image/gif" }, - { "gram", "application/srgs" }, - { "grxml", "application/srgs+xml" }, - { "gtar", "application/x-gtar" }, - { "hdf", "application/x-hdf" }, - { "hqx", "application/mac-binhex40" }, - { "htm", "text/html" }, - { "html", "text/html" }, - { "ice", "x-conference/x-cooltalk" }, - { "ico", "image/x-icon" }, - { "ics", "text/calendar" }, - { "ief", "image/ief" }, - { "ifb", "text/calendar" }, - { "iges", "model/iges" }, - { "igs", "model/iges" }, - { "jnlp", "application/x-java-jnlp-file" }, - { "jp2", "image/jp2" }, - { "jpe", "image/jpeg" }, - { "jpeg", "image/jpeg" }, - { "jpg", "image/jpeg" }, - { "js", "application/x-javascript" }, - { "kar", "audio/midi" }, - { "latex", "application/x-latex" }, - { "lha", "application/octet-stream" }, - { "lzh", "application/octet-stream" }, - { "m3u", "audio/x-mpegurl" }, - { "m4a", "audio/mp4a-latm" }, - { "m4b", "audio/mp4a-latm" }, - { "m4p", "audio/mp4a-latm" }, - { "m4u", "video/vnd.mpegurl" }, - { "m4v", "video/x-m4v" }, - { "mac", "image/x-macpaint" }, - { "man", "application/x-troff-man" }, - { "mathml", "application/mathml+xml" }, - { "me", "application/x-troff-me" }, - { "mesh", "model/mesh" }, - { "mid", "audio/midi" }, - { "midi", "audio/midi" }, - { "mif", "application/vnd.mif" }, - { "mov", "video/quicktime" }, - { "movie", "video/x-sgi-movie" }, - { "mp2", "audio/mpeg" }, - { "mp3", "audio/mpeg" }, - { "mp4", "video/mp4" }, - { "mpe", "video/mpeg" }, - { "mpeg", "video/mpeg" }, - { "mpg", "video/mpeg" }, - { "mpga", "audio/mpeg" }, - { "ms", "application/x-troff-ms" }, - { "msh", "model/mesh" }, - { "mxu", "video/vnd.mpegurl" }, - { "nc", "application/x-netcdf" }, - { "oda", "application/oda" }, - { "ogg", "application/ogg" }, - { "pbm", "image/x-portable-bitmap" }, - { "pct", "image/pict" }, - { "pdb", "chemical/x-pdb" }, - { "pdf", "application/pdf" }, - { "pgm", "image/x-portable-graymap" }, - { "pgn", "application/x-chess-pgn" }, - { "pic", "image/pict" }, - { "pict", "image/pict" }, - { "png", "image/png" }, - { "pnm", "image/x-portable-anymap" }, - { "pnt", "image/x-macpaint" }, - { "pntg", "image/x-macpaint" }, - { "ppm", "image/x-portable-pixmap" }, - { "ppt", "application/vnd.ms-powerpoint" }, - { "pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation" }, - { "potx", "application/vnd.openxmlformats-officedocument.presentationml.template" }, - { "ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow" }, - { "ppam", "application/vnd.ms-powerpoint.addin.macroEnabled.12" }, - { "pptm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12" }, - { "potm", "application/vnd.ms-powerpoint.template.macroEnabled.12" }, - { "ppsm", "application/vnd.ms-powerpoint.slideshow.macroEnabled.12" }, - { "ps", "application/postscript" }, - { "qt", "video/quicktime" }, - { "qti", "image/x-quicktime" }, - { "qtif", "image/x-quicktime" }, - { "ra", "audio/x-pn-realaudio" }, - { "ram", "audio/x-pn-realaudio" }, - { "ras", "image/x-cmu-raster" }, - { "rdf", "application/rdf+xml" }, - { "rgb", "image/x-rgb" }, - { "rm", "application/vnd.rn-realmedia" }, - { "roff", "application/x-troff" }, - { "rtf", "text/rtf" }, - { "rtx", "text/richtext" }, - { "sgm", "text/sgml" }, - { "sgml", "text/sgml" }, - { "sh", "application/x-sh" }, - { "shar", "application/x-shar" }, - { "silo", "model/mesh" }, - { "sit", "application/x-stuffit" }, - { "skd", "application/x-koan" }, - { "skm", "application/x-koan" }, - { "skp", "application/x-koan" }, - { "skt", "application/x-koan" }, - { "smi", "application/smil" }, - { "smil", "application/smil" }, - { "snd", "audio/basic" }, - { "so", "application/octet-stream" }, - { "spl", "application/x-futuresplash" }, - { "src", "application/x-wais-Source" }, - { "sv4cpio", "application/x-sv4cpio" }, - { "sv4crc", "application/x-sv4crc" }, - { "svg", "image/svg+xml" }, - { "swf", "application/x-shockwave-flash" }, - { "t", "application/x-troff" }, - { "tar", "application/x-tar" }, - { "tcl", "application/x-tcl" }, - { "tex", "application/x-tex" }, - { "texi", "application/x-texinfo" }, - { "texinfo", "application/x-texinfo" }, - { "tif", "image/tiff" }, - { "tiff", "image/tiff" }, - { "tr", "application/x-troff" }, - { "tsv", "text/tab-separated-values" }, - { "txt", "text/plain" }, - { "ustar", "application/x-ustar" }, - { "vcd", "application/x-cdlink" }, - { "vrml", "model/vrml" }, - { "vxml", "application/voicexml+xml" }, - { "wav", "audio/x-wav" }, - { "wbmp", "image/vnd.wap.wbmp" }, - { "wbmxl", "application/vnd.wap.wbxml" }, - { "wml", "text/vnd.wap.wml" }, - { "wmlc", "application/vnd.wap.wmlc" }, - { "wmls", "text/vnd.wap.wmlscript" }, - { "wmlsc", "application/vnd.wap.wmlscriptc" }, - { "wrl", "model/vrml" }, - { "xbm", "image/x-xbitmap" }, - { "xht", "application/xhtml+xml" }, - { "xhtml", "application/xhtml+xml" }, - { "xls", "application/vnd.ms-excel" }, - { "xml", "application/xml" }, - { "xpm", "image/x-xpixmap" }, - { "xsl", "application/xml" }, - { "xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" }, - { "xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template" }, - { "xlsm", "application/vnd.ms-excel.sheet.macroEnabled.12" }, - { "xltm", "application/vnd.ms-excel.template.macroEnabled.12" }, - { "xlam", "application/vnd.ms-excel.addin.macroEnabled.12" }, - { "xlsb", "application/vnd.ms-excel.sheet.binary.macroEnabled.12" }, - { "xslt", "application/xslt+xml" }, - { "xul", "application/vnd.mozilla.xul+xml" }, - { "xwd", "image/x-xwindowdump" }, - { "xyz", "chemical/x-xyz" }, - { "zip", "application/zip" } - }; - } -} diff --git a/Storage/Storage/Public/AVGeoDistance.cs b/Storage/Storage/Public/AVGeoDistance.cs deleted file mode 100644 index 7c8fdcf..0000000 --- a/Storage/Storage/Public/AVGeoDistance.cs +++ /dev/null @@ -1,68 +0,0 @@ -namespace LeanCloud { - /// - /// Represents a distance between two AVGeoPoints. - /// - public struct AVGeoDistance { - private const double EarthMeanRadiusKilometers = 6371.0; - private const double EarthMeanRadiusMiles = 3958.8; - - /// - /// Creates a AVGeoDistance. - /// - /// The distance in radians. - public AVGeoDistance(double radians) - : this() { - Radians = radians; - } - - /// - /// Gets the distance in radians. - /// - public double Radians { get; private set; } - - /// - /// Gets the distance in miles. - /// - public double Miles { - get { - return Radians * EarthMeanRadiusMiles; - } - } - - /// - /// Gets the distance in kilometers. - /// - public double Kilometers { - get { - return Radians * EarthMeanRadiusKilometers; - } - } - - /// - /// Gets a AVGeoDistance from a number of miles. - /// - /// The number of miles. - /// A AVGeoDistance for the given number of miles. - public static AVGeoDistance FromMiles(double miles) { - return new AVGeoDistance(miles / EarthMeanRadiusMiles); - } - - /// - /// Gets a AVGeoDistance from a number of kilometers. - /// - /// The number of kilometers. - /// A AVGeoDistance for the given number of kilometers. - public static AVGeoDistance FromKilometers(double kilometers) { - return new AVGeoDistance(kilometers / EarthMeanRadiusKilometers); - } - - /// - /// Gets a AVGeoDistance from a number of radians. - /// - /// The number of radians. - /// A AVGeoDistance for the given number of radians. - public static AVGeoDistance FromRadians(double radians) { - return new AVGeoDistance(radians); - } - } -} diff --git a/Storage/Storage/Public/AVGeoPoint.cs b/Storage/Storage/Public/AVGeoPoint.cs deleted file mode 100644 index cefb19a..0000000 --- a/Storage/Storage/Public/AVGeoPoint.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System; -using System.Collections.Generic; -using LeanCloud.Storage.Internal; - -namespace LeanCloud { - /// - /// AVGeoPoint represents a latitude / longitude point that may be associated - /// with a key in a AVObject or used as a reference point for geo queries. - /// This allows proximity-based queries on the key. - /// - /// Only one key in a class may contain a GeoPoint. - /// - public struct AVGeoPoint : IJsonConvertible { - /// - /// Constructs a AVGeoPoint with the specified latitude and longitude. - /// - /// The point's latitude. - /// The point's longitude. - public AVGeoPoint(double latitude, double longitude) - : this() { - Latitude = latitude; - Longitude = longitude; - } - - private double latitude; - /// - /// Gets or sets the latitude of the GeoPoint. Valid range is [-90, 90]. - /// Extremes should not be used. - /// - public double Latitude { - get { - return latitude; - } - set { - if (value > 90 || value < -90) { - throw new ArgumentOutOfRangeException(nameof(value), "Latitude must be within the range [-90, 90]"); - } - latitude = value; - } - } - - private double longitude; - /// - /// Gets or sets the longitude. Valid range is [-180, 180]. - /// Extremes should not be used. - /// - public double Longitude { - get { - return longitude; - } - set { - if (value > 180 || value < -180) { - throw new ArgumentOutOfRangeException(nameof(value), "Longitude must be within the range [-180, 180]"); - } - longitude = value; - } - } - - /// - /// Get the distance in radians between this point and another GeoPoint. This is the smallest angular - /// distance between the two points. - /// - /// GeoPoint describing the other point being measured against. - /// The distance in between the two points. - public AVGeoDistance DistanceTo(AVGeoPoint point) { - double d2r = Math.PI / 180; // radian conversion factor - double lat1rad = Latitude * d2r; - double long1rad = longitude * d2r; - double lat2rad = point.Latitude * d2r; - double long2rad = point.Longitude * d2r; - double deltaLat = lat1rad - lat2rad; - double deltaLong = long1rad - long2rad; - double sinDeltaLatDiv2 = Math.Sin(deltaLat / 2); - double sinDeltaLongDiv2 = Math.Sin(deltaLong / 2); - // Square of half the straight line chord distance between both points. - // [0.0, 1.0] - double a = sinDeltaLatDiv2 * sinDeltaLatDiv2 + - Math.Cos(lat1rad) * Math.Cos(lat2rad) * sinDeltaLongDiv2 * sinDeltaLongDiv2; - a = Math.Min(1.0, a); - return new AVGeoDistance(2 * Math.Asin(Math.Sqrt(a))); - } - - IDictionary IJsonConvertible.ToJSON() { - return new Dictionary { - { "__type", "GeoPoint" }, - { "latitude", Latitude }, - { "longitude", Longitude } - }; - } - } -} diff --git a/Storage/Storage/Public/AVObject.cs b/Storage/Storage/Public/AVObject.cs deleted file mode 100644 index 48d3c67..0000000 --- a/Storage/Storage/Public/AVObject.cs +++ /dev/null @@ -1,802 +0,0 @@ -using LeanCloud.Storage.Internal; -using LeanCloud.Utilities; -using System; -using System.Text; -using System.Collections.Generic; -using System.Net.Http; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using System.Collections; - -namespace LeanCloud { - public class AVObject : IEnumerable> { - static readonly HashSet RESERVED_KEYS = new HashSet { - "objectId", "ACL", "createdAt", "updatedAt" - }; - - public string ClassName { - get { - return state.ClassName; - } - } - - [AVFieldName("objectId")] - public string ObjectId { - get { - return state.ObjectId; - } - set { - IsDirty = true; - MutateState(mutableClone => { - mutableClone.ObjectId = value; - }); - } - } - - internal AVACL acl; - - [AVFieldName("ACL")] - public AVACL ACL { - // 设置 IsDirty - get { - return acl; - } set { - acl = value; - IsDirty = true; - } - } - - [AVFieldName("createdAt")] - public DateTime? CreatedAt { - get { - return state.CreatedAt; - } - } - - [AVFieldName("updatedAt")] - public DateTime? UpdatedAt { - get { - return state.UpdatedAt; - } - } - - public ICollection Keys { - get { - return estimatedData.Keys; - } - } - - private static readonly string AutoClassName = "_Automatic"; - - internal readonly Dictionary operationDict = new Dictionary(); - private Dictionary serverData = new Dictionary(); - private Dictionary estimatedData = new Dictionary(); - - private bool dirty; - - private IObjectState state; - - internal void MutateState(Action func) { - state = state.MutatedClone(func); - RebuildEstimatedData(); - } - - public IObjectState State { - get { - return state; - } - } - - internal static ObjectSubclassingController SubclassingController { - get { - return AVPlugins.Instance.SubclassingController; - } - } - - public static string GetSubClassName() { - return SubclassingController.GetClassName(typeof(TAVObject)); - } - - #region AVObject Creation - - protected AVObject() - : this(AutoClassName) { - } - - public AVObject(string className) { - if (className == null) { - throw new ArgumentException("You must specify a LeanCloud class name when creating a new AVObject."); - } - if (AutoClassName.Equals(className)) { - className = SubclassingController.GetClassName(GetType()); - } - if (!SubclassingController.IsTypeValid(className, GetType())) { - throw new ArgumentException( - "You must create this type of AVObject using AVObject.Create() or the proper subclass."); - } - state = new MutableObjectState { - ClassName = className - }; - IsDirty = true; - } - - public static AVObject Create(string className) { - return SubclassingController.Instantiate(className); - } - - public static AVObject CreateWithoutData(string className, string objectId) { - var result = SubclassingController.Instantiate(className); - result.ObjectId = objectId; - result.IsDirty = false; - if (result.IsDirty) { - throw new InvalidOperationException("A AVObject subclass default constructor must not make changes to the object that cause it to be dirty."); - } - return result; - } - - public static T Create() where T : AVObject { - return (T)SubclassingController.Instantiate(SubclassingController.GetClassName(typeof(T))); - } - - public static T CreateWithoutData(string objectId) where T : AVObject { - return (T)CreateWithoutData(SubclassingController.GetClassName(typeof(T)), objectId); - } - - public static T FromState(IObjectState state, string defaultClassName) where T : AVObject { - string className = state.ClassName ?? defaultClassName; - - T obj = (T)CreateWithoutData(className, state.ObjectId); - obj.HandleFetchResult(state); - - return obj; - } - - #endregion - - public static IDictionary GetPropertyMappings(string className) { - return SubclassingController.GetPropertyMappings(className); - } - - private static string GetFieldForPropertyName(string className, string propertyName) { - if (SubclassingController.GetPropertyMappings(className).TryGetValue(propertyName, out string fieldName)) { - return fieldName; - } - return null; - } - - protected virtual void SetProperty(T value, string propertyName) { - this[GetFieldForPropertyName(ClassName, propertyName)] = value; - } - - protected AVRelation GetRelationProperty(string propertyName) where T : AVObject { - return GetRelation(GetFieldForPropertyName(ClassName, propertyName)); - } - - protected virtual T GetProperty(string propertyName) { - return GetProperty(default, propertyName); - } - - protected virtual T GetProperty(T defaultValue, string propertyName) { - if (TryGetValue(GetFieldForPropertyName(ClassName, propertyName), out T result)) { - return result; - } - return defaultValue; - } - - internal virtual void SetDefaultValues() { - } - - public static void RegisterSubclass() where T : AVObject, new() { - SubclassingController.RegisterSubclass(typeof(T)); - } - - internal static void UnregisterSubclass() where T : AVObject, new() { - SubclassingController.UnregisterSubclass(typeof(T)); - } - - public void Revert() { - if (operationDict.Any()) { - operationDict.Clear(); - RebuildEstimatedData(); - } - } - - internal virtual void HandleFetchResult(IObjectState serverState) { - MergeFromServer(serverState); - } - - internal virtual void HandleSave(IObjectState serverState) { - state = state.MutatedClone((objectState) => objectState.Apply(operationDict)); - MergeFromServer(serverState); - } - - public virtual void MergeFromServer(IObjectState serverState) { - // Make a new serverData with fetched values. - var newServerData = serverState.ToDictionary(t => t.Key, t => t.Value); - - // We cache the fetched object because subsequent Save operation might flush - // the fetched objects into Pointers. - //IDictionary fetchedObject = CollectFetchedObjects(); - - //foreach (var pair in serverState) { - // var value = pair.Value; - // if (value is AVObject) { - // // Resolve fetched object. - // var avObject = value as AVObject; - // if (fetchedObject.TryGetValue(avObject.ObjectId, out AVObject obj)) { - // value = obj; - // } - // } - // newServerData[pair.Key] = value; - //} - - this.serverData = serverData.Concat(this.serverData.Where(d => !serverData.ContainsKey(d.Key))) - .ToDictionary(x => x.Key, x => x.Value); - - IsDirty = false; - serverState = serverState.MutatedClone(mutableClone => { - mutableClone.ServerData = newServerData; - }); - MutateState(mutableClone => { - mutableClone.Apply(serverState); - }); - } - - internal void MergeFromObject(AVObject other) { - if (this == other) { - return; - } - - operationDict.Clear(); - foreach (KeyValuePair entry in other.operationDict) { - operationDict.Add(entry.Key, entry.Value); - } - state = other.State; - - RebuildEstimatedData(); - } - - internal IDictionary ToJSONObject() { - IDictionary result = new Dictionary(); - if (ACL != null) { - result["ACL"] = PointerOrLocalIdEncoder.Instance.Encode(ACL); - } - foreach (KeyValuePair kv in operationDict) { - result[kv.Key] = PointerOrLocalIdEncoder.Instance.Encode(kv.Value); - } - return result; - } - - public static IDictionary ToJSONObjectForSaving(IDictionary operations) { - var result = new Dictionary(); - foreach (var pair in operations) { - // AVRPCSerialize the data - var operation = pair.Value; - - result[pair.Key] = PointerOrLocalIdEncoder.Instance.Encode(operation); - } - return result; - } - - internal IDictionary EncodeForSaving(IDictionary data) { - var result = new Dictionary(); - foreach (var key in data.Keys) { - var value = data[key]; - result.Add(key, PointerOrLocalIdEncoder.Instance.Encode(value)); - } - - return result; - } - - - internal IDictionary ServerDataToJSONObjectForSerialization() { - return PointerOrLocalIdEncoder.Instance.Encode(state.ToDictionary(t => t.Key, t => t.Value)) - as IDictionary; - } - - internal virtual async Task FetchAsyncInternal(IDictionary queryString, CancellationToken cancellationToken = default) { - if (ObjectId == null) { - throw new InvalidOperationException("Cannot refresh an object that hasn't been saved to the server."); - } - if (queryString == null) { - queryString = new Dictionary(); - } - - var command = new AVCommand { - Path = $"classes/{Uri.EscapeDataString(state.ClassName)}/{Uri.EscapeDataString(state.ObjectId)}?{AVClient.BuildQueryString(queryString)}", - Method = HttpMethod.Get - }; - var data = await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command, cancellationToken); - IObjectState objectState = AVObjectCoder.Instance.Decode(data.Item2, AVDecoder.Instance); - - HandleFetchResult(objectState); - return this; - } - - #region Save - - public virtual async Task SaveAsync(bool fetchWhenSave = false, AVQuery query = null, CancellationToken cancellationToken = default) { - if (HasCircleReference(this, new HashSet())) { - throw new AVException(AVException.ErrorCode.CircleReference, "Found a circle dependency when save"); - } - Stack batches = BatchObjects(new List { this }, false); - await SaveBatches(batches, cancellationToken); - - IDictionary objectJSON = ToJSONObject(); - var command = new AVCommand { - Path = ObjectId == null ? $"classes/{Uri.EscapeDataString(ClassName)}" : - $"classes/{Uri.EscapeDataString(ClassName)}/{ObjectId}", - Method = ObjectId == null ? HttpMethod.Post : HttpMethod.Put, - Content = objectJSON - }; - Dictionary args = new Dictionary(); - if (fetchWhenSave) { - args.Add("fetchWhenSave", fetchWhenSave); - } - // 查询条件 - if (query != null) { - args.Add("where", query.BuildWhere()); - } - if (args.Count > 0) { - string encode = AVClient.BuildQueryString(args); - command.Path = $"{command.Path}?{encode}"; - } - var data = await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command, cancellationToken); - IObjectState serverState = AVObjectCoder.Instance.Decode(data.Item2, AVDecoder.Instance); - HandleSave(serverState); - } - - public static async Task SaveAllAsync(IEnumerable objects, CancellationToken cancellationToken = default) - where T : AVObject { - foreach (T obj in objects) { - if (HasCircleReference(obj, new HashSet())) { - throw new AVException(AVException.ErrorCode.CircleReference, "Found a circle dependency when save"); - } - } - Stack batches = BatchObjects(objects, true); - await SaveBatches(batches, cancellationToken); - } - - static async Task SaveBatches(Stack batches, CancellationToken cancellationToken = default) { - while (batches.Any()) { - Batch batch = batches.Pop(); - IList dirtyObjects = batch.Objects.Where(o => o.IsDirty).ToList(); - - List commandList = new List(); - foreach (AVObject avObj in dirtyObjects) { - AVCommand command = new AVCommand { - Path = avObj.ObjectId == null ? $"classes/{Uri.EscapeDataString(avObj.ClassName)}" : - $"classes/{Uri.EscapeDataString(avObj.ClassName)}/{Uri.EscapeDataString(avObj.ObjectId)}", - Method = avObj.ObjectId == null ? HttpMethod.Post : HttpMethod.Put, - Content = avObj.ToJSONObject() - }; - commandList.Add(command); - } - IList list = new List(); - var result = await AVPlugins.Instance.CommandRunner.ExecuteBatchRequests(commandList, cancellationToken); - foreach (var data in result) { - if (data.TryGetValue("success", out object val)) { - IObjectState obj = AVObjectCoder.Instance.Decode(val as IDictionary, AVDecoder.Instance); - list.Add(obj); - } - } - - try { - foreach (var pair in dirtyObjects.Zip(list, (item, state) => new { item, state })) { - pair.item.HandleSave(pair.state); - } - } catch (Exception e) { - throw e; - } - } - } - - #endregion - - private static Task> FetchAllInternalAsync( - IEnumerable objects, bool force, CancellationToken cancellationToken) where T : AVObject { - - if (objects.Any(obj => obj.state.ObjectId == null)) { - throw new InvalidOperationException("You cannot fetch objects that haven't already been saved."); - } - - // Do one Find for each class. - var findsByClass = - (from obj in objects - group obj.ObjectId by obj.ClassName into classGroup - where classGroup.Any() - select new { - ClassName = classGroup.Key, - FindTask = new AVQuery(classGroup.Key) - .WhereContainedIn("objectId", classGroup) - .FindAsync(cancellationToken) - }).ToDictionary(pair => pair.ClassName, pair => pair.FindTask); - - // Wait for all the Finds to complete. - return Task.WhenAll(findsByClass.Values.ToList()).OnSuccess(__ => { - if (cancellationToken.IsCancellationRequested) { - return objects; - } - - // Merge the data from the Finds into the input objects. - var pairs = from obj in objects - from result in findsByClass[obj.ClassName].Result - where result.ObjectId == obj.ObjectId - select new { obj, result }; - foreach (var pair in pairs) { - pair.obj.MergeFromObject(pair.result); - } - - return objects; - }); - } - - #region Fetch - - public static Task> FetchAllIfNeededAsync( - IEnumerable objects, CancellationToken cancellationToken = default) where T : AVObject { - return FetchAllInternalAsync(objects, false, cancellationToken); - } - - public static Task> FetchAllAsync(IEnumerable objects, CancellationToken cancellationToken = default) where T : AVObject { - return FetchAllInternalAsync(objects, true, cancellationToken); - } - - #endregion - - #region Delete - - public virtual async Task DeleteAsync(AVQuery query = null, CancellationToken cancellationToken = default) { - if (ObjectId == null) { - return; - } - var command = new AVCommand { - Path = $"classes/{state.ClassName}/{state.ObjectId}", - Method = HttpMethod.Delete - }; - if (query != null) { - Dictionary where = new Dictionary { - { "where", query.BuildWhere() } - }; - command.Path = $"{command.Path}?{AVClient.BuildQueryString(where)}"; - } - await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command, cancellationToken); - IsDirty = true; - } - - public static async Task DeleteAllAsync(IEnumerable objects, CancellationToken cancellationToken = default) - where T : AVObject { - var uniqueObjects = new HashSet(objects.OfType().ToList(), - new IdentityEqualityComparer()); - - var states = uniqueObjects.Select(t => t.state); - var requests = states - .Where(item => item.ObjectId != null) - .Select(item => new AVCommand { - Path = $"classes/{Uri.EscapeDataString(item.ClassName)}/{Uri.EscapeDataString(item.ObjectId)}", - Method = HttpMethod.Delete - }) - .ToList(); - await AVPlugins.Instance.CommandRunner.ExecuteBatchRequests(requests, cancellationToken); - - foreach (var obj in uniqueObjects) { - obj.IsDirty = true; - } - } - - #endregion - - public virtual void Remove(string key) { - CheckKeyValid(key); - PerformOperation(key, AVDeleteOperation.Instance); - } - - virtual public object this[string key] { - get { - CheckKeyValid(key); - - if (!estimatedData.TryGetValue(key, out object value)) { - value = state[key]; - } - - if (value is AVRelationBase) { - var relation = value as AVRelationBase; - relation.EnsureParentAndKey(this, key); - } - - return value; - } - set { - CheckKeyValid(key); - Set(key, value); - } - } - - #region Atomic Increment - - public void Increment(string key) { - Increment(key, 1); - } - - public void Increment(string key, long amount) { - CheckKeyValid(key); - PerformOperation(key, new AVIncrementOperation(amount)); - } - - public void Increment(string key, double amount) { - CheckKeyValid(key); - PerformOperation(key, new AVIncrementOperation(amount)); - } - - #endregion - - #region List - - public void AddToList(string key, object value) { - CheckKeyValid(key); - AddRangeToList(key, new[] { value }); - } - - public void AddRangeToList(string key, IEnumerable values) { - CheckKeyValid(key); - PerformOperation(key, new AVAddOperation(values.Cast())); - } - - public void AddUniqueToList(string key, object value) { - CheckKeyValid(key); - AddRangeUniqueToList(key, new object[] { value }); - } - - public void AddRangeUniqueToList(string key, IEnumerable values) { - CheckKeyValid(key); - PerformOperation(key, new AVAddUniqueOperation(values.Cast())); - } - - public void RemoveAllFromList(string key, IEnumerable values) { - CheckKeyValid(key); - PerformOperation(key, new AVRemoveOperation(values.Cast())); - } - - #endregion - - - private IEnumerable ApplyOperations(IDictionary operations, IDictionary map) { - List appliedKeys = new List(); - foreach (var pair in operations) { - map.TryGetValue(pair.Key, out object oldValue); - var newValue = pair.Value.Apply(oldValue, pair.Key); - if (newValue != AVDeleteOperation.DeleteToken) { - map[pair.Key] = newValue; - } else { - map.Remove(pair.Key); - } - appliedKeys.Add(pair.Key); - } - return appliedKeys; - } - - internal void RebuildEstimatedData() { - estimatedData.Clear(); - foreach (KeyValuePair entry in serverData) { - estimatedData.Add(entry.Key, entry.Value); - } - ApplyOperations(operationDict, estimatedData); - } - - internal void PerformOperation(string key, IAVFieldOperation operation) { - estimatedData.TryGetValue(key, out object oldValue); - object newValue = operation.Apply(oldValue, key); - if (newValue != AVDeleteOperation.DeleteToken) { - estimatedData[key] = newValue; - } else { - estimatedData.Remove(key); - } - - if (operationDict.TryGetValue(key, out IAVFieldOperation oldOperation)) { - operation = operation.MergeWithPrevious(oldOperation); - } - operationDict[key] = operation; - } - - internal virtual void OnSettingValue(ref string key, ref object value) { - if (key == null) { - throw new ArgumentNullException(nameof(key)); - } - } - - internal void Set(string key, object value) { - OnSettingValue(ref key, ref value); - PerformOperation(key, new AVSetOperation(value)); - } - - void CheckKeyValid(string key) { - if (string.IsNullOrEmpty(key)) { - throw new ArgumentNullException(nameof(key)); - } - if (key.StartsWith("_", StringComparison.CurrentCulture)) { - throw new ArgumentException("key should not start with _"); - } - if (RESERVED_KEYS.Contains(key)) { - throw new ArgumentException($"key: {key} is reserved by LeanCloud"); - } - } - - - - public void Add(string key, object value) { - if (ContainsKey(key)) { - throw new ArgumentException("Key already exists", key); - } - this[key] = value; - } - - public bool ContainsKey(string key) { - return estimatedData.ContainsKey(key) || state.ContainsKey(key); - } - - public T Get(string key) { - return Conversion.To(this[key]); - } - - public AVRelation GetRelation(string key) where T : AVObject { - // All the sanity checking is done when add or remove is called. - TryGetValue(key, out AVRelation relation); - return relation ?? new AVRelation(this, key); - } - - public AVQuery GetRelationRevserseQuery(string parentClassName, string key) where T : AVObject { - if (string.IsNullOrEmpty(parentClassName)) { - throw new ArgumentNullException(nameof(parentClassName), "can not query a relation without parentClassName."); - } - if (string.IsNullOrEmpty(key)) { - throw new ArgumentNullException(nameof(key), "can not query a relation without key."); - } - return new AVQuery(parentClassName).WhereEqualTo(key, this); - } - - public virtual bool TryGetValue(string key, out T result) { - if (ContainsKey(key)) { - try { - var temp = Conversion.To(this[key]); - result = temp; - return true; - } catch (InvalidCastException) { - result = default; - return false; - } - } - result = default; - return false; - } - - public bool HasSameId(AVObject other) { - return other != null && - object.Equals(ClassName, other.ClassName) && - object.Equals(ObjectId, other.ObjectId); - } - - public bool IsDirty { - get { - return CheckIsDirty(); - } - internal set { - dirty = value; - } - } - - public bool IsKeyDirty(string key) { - return operationDict.ContainsKey(key); - } - - private bool CheckIsDirty() { - return dirty || operationDict.Count > 0; - } - - IEnumerator> IEnumerable>.GetEnumerator() { - return estimatedData.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() { - return ((IEnumerable>)this).GetEnumerator(); - } - - public static AVQuery GetQuery(string className) - where T : AVObject { - return new AVQuery(className); - } - - - static bool HasCircleReference(object obj, HashSet parents) { - if (parents.Contains(obj)) { - return true; - } - IEnumerable deps = null; - if (obj is IList) { - deps = obj as IList; - } else if (obj is IDictionary) { - deps = (obj as IDictionary).Values; - } else if (obj is AVObject) { - deps = (obj as AVObject).estimatedData.Values; - } - HashSet depParent = new HashSet(parents); - if (obj is AVObject) { - depParent.Add(obj as AVObject); - } - if (deps != null) { - foreach (object dep in deps) { - HashSet p = new HashSet(depParent); - if (HasCircleReference(dep, p)) { - return true; - } - } - } - return false; - } - - static Stack BatchObjects(IEnumerable avObjects, bool containsSelf) { - Stack batches = new Stack(); - if (containsSelf) { - batches.Push(new Batch(avObjects)); - } - - IEnumerable deps = avObjects.Select(avObj => avObj.estimatedData.Values); - do { - HashSet childSets = new HashSet(); - foreach (object dep in deps) { - IEnumerable children = null; - if (dep is IList) { - children = dep as IList; - } else if (dep is IDictionary) { - children = (dep as IDictionary).Values; - } else if (dep is AVObject && (dep as AVObject).ObjectId == null) { - // 如果依赖是 AVObject 类型并且还没有保存过,则应该遍历其依赖 - // 这里应该是从 Operation 中查找新增的对象 - children = (dep as AVObject).estimatedData.Values; - } - if (children != null) { - foreach (object child in children) { - childSets.Add(child); - } - } - } - IEnumerable depAVObjs = deps.OfType().Where(o => o.ObjectId == null); - if (depAVObjs.Any()) { - batches.Push(new Batch(depAVObjs)); - } - deps = childSets; - } while (deps != null && deps.Any()); - - return batches; - } - - /// - /// 保存 AVObject 时用到的辅助批次工具类 - /// - internal class Batch { - internal HashSet Objects { - get; set; - } - - public Batch() { - Objects = new HashSet(); - } - - public Batch(IEnumerable objects) : this() { - foreach (AVObject obj in objects) { - Objects.Add(obj); - } - } - - public override string ToString() { - StringBuilder sb = new StringBuilder(); - sb.AppendLine("----------------------------"); - foreach (AVObject obj in Objects) { - sb.AppendLine(obj.ClassName); - } - sb.AppendLine("----------------------------"); - return sb.ToString(); - } - } - } -} diff --git a/Storage/Storage/Public/AVQuery.cs b/Storage/Storage/Public/AVQuery.cs deleted file mode 100644 index a8bbd74..0000000 --- a/Storage/Storage/Public/AVQuery.cs +++ /dev/null @@ -1,347 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using System.Net.Http; -using System.Text.RegularExpressions; -using Newtonsoft.Json; -using LeanCloud.Storage.Internal; - -namespace LeanCloud { - public class AVQuery where T : AVObject { - public string ClassName { - get; internal set; - } - - private string path; - public string Path { - get { - if (string.IsNullOrEmpty(path)) { - return $"classes/{Uri.EscapeDataString(ClassName)}"; - } - return path; - } - set { - path = value; - } - } - - /// - /// 根查询条件,默认是 and 查询,可以设置为 or 查询 - /// - QueryCombinedCondition condition; - - static AVQueryController QueryController { - get { - return AVPlugins.Instance.QueryController; - } - } - - public AVQuery() - : this(AVPlugins.Instance.SubclassingController.GetClassName(typeof(T))) { - } - - public AVQuery(string className) { - if (string.IsNullOrEmpty(className)) { - throw new ArgumentNullException(nameof(className)); - } - ClassName = className; - condition = new QueryCombinedCondition(); - } - - #region Combined Query - - public static AVQuery And(IEnumerable> queries) { - AVQuery composition = new AVQuery(); - string className = null; - if (queries != null) { - foreach (AVQuery query in queries) { - if (className != null && className != query.ClassName) { - throw new ArgumentException("All of the queries in an or query must be on the same class."); - } - composition.condition.AddCondition(query.condition); - className = query.ClassName; - } - } - composition.ClassName = className; - return composition; - } - - public static AVQuery Or(IEnumerable> queries) { - AVQuery composition = new AVQuery { - condition = new QueryCombinedCondition(QueryCombinedCondition.OR) - }; - string className = null; - if (queries != null) { - foreach (AVQuery query in queries) { - if (className != null && className != query.ClassName) { - throw new ArgumentException("All of the queries in an or query must be on the same class."); - } - composition.condition.AddCondition(query.condition); - className = query.ClassName; - } - } - composition.ClassName = className; - return composition; - } - - #endregion - - public virtual async Task> FindAsync(CancellationToken cancellationToken = default) { - IEnumerable states = await QueryController.FindAsync(this, cancellationToken); - return (from state in states - select AVObject.FromState(state, ClassName)); - } - - public virtual async Task FirstOrDefaultAsync(CancellationToken cancellationToken = default) { - IObjectState state = await QueryController.FirstAsync(this, cancellationToken); - return state == null ? default : AVObject.FromState(state, ClassName); - } - - public virtual async Task FirstAsync(CancellationToken cancellationToken = default) { - var result = await FirstOrDefaultAsync(cancellationToken); - if (result == null) { - throw new AVException(AVException.ErrorCode.ObjectNotFound, - "No results matched the query."); - } - return result; - } - - public virtual Task CountAsync(CancellationToken cancellationToken = default) { - return QueryController.CountAsync(this, cancellationToken); - } - - public virtual async Task GetAsync(string objectId, CancellationToken cancellationToken) { - WhereEqualTo("objectId", objectId); - Limit(1); - var result = await FindAsync(cancellationToken); - var first = result.FirstOrDefault(); - if (first == null) { - throw new AVException(AVException.ErrorCode.ObjectNotFound, - "Object with the given objectId not found."); - } - return first; - } - - #region CQL - - /// - /// 执行 CQL 查询 - /// - /// CQL 语句 - /// CancellationToken - /// 返回符合条件的对象集合 - public static Task> DoCloudQueryAsync(string cql, CancellationToken cancellationToken = default) { - var queryString = $"cloudQuery?cql={Uri.EscapeDataString(cql)}"; - return RebuildObjectFromCloudQueryResult(queryString); - } - - /// - /// 执行 CQL 查询 - /// - /// 带有占位符的模板 cql 语句 - /// 占位符对应的参数数组 - /// - public static Task> DoCloudQueryAsync(string cqlTeamplate, params object[] pvalues) { - string queryStringTemplate = "cloudQuery?cql={0}&pvalues={1}"; - string pSrting = JsonConvert.SerializeObject(pvalues); - string queryString = string.Format(queryStringTemplate, Uri.EscapeDataString(cqlTeamplate), Uri.EscapeDataString(pSrting)); - - return RebuildObjectFromCloudQueryResult(queryString); - } - - internal static async Task> RebuildObjectFromCloudQueryResult(string queryString) { - var command = new AVCommand { - Path = queryString, - Method = HttpMethod.Get - }; - var result = await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command, CancellationToken.None); - var items = result.Item2["results"] as IList; - var className = result.Item2["className"].ToString(); - - IEnumerable states = (from item in items - select AVObjectCoder.Instance.Decode(item as IDictionary, AVDecoder.Instance)); - - return from state in states - select AVObject.FromState(state, className); - } - - #endregion - - public AVQuery OrderBy(string key) { - condition.OrderBy(key); - return this; - } - - public AVQuery OrderByDescending(string key) { - condition.OrderByDescending(key); - return this; - } - - public AVQuery Include(string key) { - condition.Include(key); - return this; - } - - public AVQuery Select(string key) { - condition.Select(key); - return this; - } - - public AVQuery Skip(int count) { - condition.Skip(count); - return this; - } - - public AVQuery Limit(int count) { - condition.Limit(count); - return this; - } - - #region Where - - public AVQuery WhereContainedIn(string key, IEnumerable values) { - condition.WhereContainedIn(key, values); - return this; - } - - public AVQuery WhereContainsAll(string key, IEnumerable values) { - condition.WhereContainsAll(key, values); - return this; - } - - public AVQuery WhereContains(string key, string substring) { - condition.WhereContains(key, substring); - return this; - } - - public AVQuery WhereDoesNotExist(string key) { - condition.WhereDoesNotExist(key); - return this; - } - - public AVQuery WhereDoesNotMatchQuery(string key, AVQuery query) where TOther : AVObject { - condition.WhereDoesNotMatchQuery(key, query); - return this; - } - - public AVQuery WhereEndsWith(string key, string suffix) { - condition.WhereEndsWith(key, suffix); - return this; - } - - public AVQuery WhereEqualTo(string key, object value) { - condition.WhereEqualTo(key, value); - return this; - } - - public AVQuery WhereSizeEqualTo(string key, uint size) { - condition.WhereSizeEqualTo(key, size); - return this; - } - - public AVQuery WhereExists(string key) { - condition.WhereExists(key); - return this; - } - - public AVQuery WhereGreaterThan(string key, object value) { - condition.WhereGreaterThan(key, value); - return this; - } - - public AVQuery WhereGreaterThanOrEqualTo(string key, object value) { - condition.WhereGreaterThanOrEqualTo(key, value); - return this; - } - - public AVQuery WhereLessThan(string key, object value) { - condition.WhereLessThan(key, value); - return this; - } - - public AVQuery WhereLessThanOrEqualTo(string key, object value) { - condition.WhereLessThanOrEqualTo(key, value); - return this; - } - - public AVQuery WhereMatches(string key, Regex regex, string modifiers) { - condition.WhereMatches(key, regex, modifiers); - return this; - } - - public AVQuery WhereMatches(string key, Regex regex) { - return WhereMatches(key, regex, null); - } - - public AVQuery WhereMatches(string key, string pattern, string modifiers) { - return WhereMatches(key, new Regex(pattern, RegexOptions.ECMAScript), modifiers); - } - - public AVQuery WhereMatches(string key, string pattern) { - return WhereMatches(key, pattern, null); - } - - public AVQuery WhereMatchesKeyInQuery(string key, string keyInQuery, AVQuery query) where TOther : AVObject { - condition.WhereMatchesKeyInQuery(key, keyInQuery, query); - return this; - } - - public AVQuery WhereDoesNotMatchesKeyInQuery(string key, string keyInQuery, AVQuery query) where TOther : AVObject { - condition.WhereDoesNotMatchesKeyInQuery(key, keyInQuery, query); - return this; - } - - public AVQuery WhereMatchesQuery(string key, AVQuery query) where TOther : AVObject { - condition.WhereMatchesQuery(key, query); - return this; - } - - public AVQuery WhereNear(string key, AVGeoPoint point) { - condition.WhereNear(key, point); - return this; - } - - public AVQuery WhereNotContainedIn(string key, IEnumerable values) { - condition.WhereNotContainedIn(key, values); - return this; - } - - public AVQuery WhereNotEqualTo(string key, object value) { - condition.WhereNotEqualTo(key, value); - return this; - } - - public AVQuery WhereStartsWith(string key, string suffix) { - condition.WhereStartsWith(key, suffix); - return this; - } - - public AVQuery WhereWithinGeoBox(string key, AVGeoPoint southwest, AVGeoPoint northeast) { - condition.WhereWithinGeoBox(key, southwest, northeast); - return this; - } - - public AVQuery WhereWithinDistance(string key, AVGeoPoint point, AVGeoDistance maxDistance) { - condition.WhereWithinDistance(key, point, maxDistance); - return this; - } - - public AVQuery WhereRelatedTo(AVObject parent, string key) { - condition.WhereRelatedTo(parent, key); - return this; - } - - #endregion - - public IDictionary BuildParameters(string className = null) { - return condition.BuildParameters(className); - } - - public IDictionary BuildWhere() { - return condition.ToJSON(); - } - } -} diff --git a/Storage/Storage/Public/AVQueryExtensions.cs b/Storage/Storage/Public/AVQueryExtensions.cs deleted file mode 100644 index e15be08..0000000 --- a/Storage/Storage/Public/AVQueryExtensions.cs +++ /dev/null @@ -1,780 +0,0 @@ -using LeanCloud.Storage.Internal; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; - -namespace LeanCloud -{ - /// - /// Provides extension methods for to support - /// Linq-style queries. - /// - public static class AVQueryExtensions - { - private static readonly MethodInfo getMethod; - private static readonly MethodInfo stringContains; - private static readonly MethodInfo stringStartsWith; - private static readonly MethodInfo stringEndsWith; - private static readonly MethodInfo containsMethod; - private static readonly MethodInfo notContainsMethod; - private static readonly MethodInfo containsKeyMethod; - private static readonly MethodInfo notContainsKeyMethod; - private static readonly Dictionary functionMappings; - static AVQueryExtensions() - { - getMethod = GetMethod(obj => obj.Get(null)).GetGenericMethodDefinition(); - stringContains = GetMethod(str => str.Contains(null)); - stringStartsWith = GetMethod(str => str.StartsWith(null)); - stringEndsWith = GetMethod(str => str.EndsWith(null)); - functionMappings = new Dictionary { - { - stringContains, - GetMethod>(q => q.WhereContains(null, null)) - }, - { - stringStartsWith, - GetMethod>(q => q.WhereStartsWith(null, null)) - }, - { - stringEndsWith, - GetMethod>(q => q.WhereEndsWith(null,null)) - }, - }; - containsMethod = GetMethod( - o => AVQueryExtensions.ContainsStub(null, null)).GetGenericMethodDefinition(); - notContainsMethod = GetMethod( - o => AVQueryExtensions.NotContainsStub(null, null)) - .GetGenericMethodDefinition(); - - containsKeyMethod = GetMethod(o => AVQueryExtensions.ContainsKeyStub(null, null)); - notContainsKeyMethod = GetMethod( - o => AVQueryExtensions.NotContainsKeyStub(null, null)); - } - - /// - /// Gets a MethodInfo for a top-level method call. - /// - private static MethodInfo GetMethod(Expression> expression) - { - return (expression.Body as MethodCallExpression).Method; - } - - /// - /// When a query is normalized, this is a placeholder to indicate we should - /// add a WhereContainedIn() clause. - /// - private static bool ContainsStub(object collection, T value) - { - throw new NotImplementedException( - "Exists only for expression translation as a placeholder."); - } - - /// - /// When a query is normalized, this is a placeholder to indicate we should - /// add a WhereNotContainedIn() clause. - /// - private static bool NotContainsStub(object collection, T value) - { - throw new NotImplementedException( - "Exists only for expression translation as a placeholder."); - } - - /// - /// When a query is normalized, this is a placeholder to indicate that we should - /// add a WhereExists() clause. - /// - private static bool ContainsKeyStub(AVObject obj, string key) - { - throw new NotImplementedException( - "Exists only for expression translation as a placeholder."); - } - - /// - /// When a query is normalized, this is a placeholder to indicate that we should - /// add a WhereDoesNotExist() clause. - /// - private static bool NotContainsKeyStub(AVObject obj, string key) - { - throw new NotImplementedException( - "Exists only for expression translation as a placeholder."); - } - - /// - /// Evaluates an expression and throws if the expression has components that can't be - /// evaluated (e.g. uses the parameter that's only represented by an object on the server). - /// - private static object GetValue(Expression exp) - { - try - { - return Expression.Lambda( - typeof(Func<>).MakeGenericType(exp.Type), exp).Compile().DynamicInvoke(); - } - catch (Exception e) - { - throw new InvalidOperationException("Unable to evaluate expression: " + exp, e); - } - } - - /// - /// Checks whether the MethodCallExpression is a call to AVObject.Get(), - /// which is the call we normalize all indexing into the AVObject to. - /// - private static bool IsAVObjectGet(MethodCallExpression node) - { - if (node == null || node.Object == null) - { - return false; - } - if (!typeof(AVObject).GetTypeInfo().IsAssignableFrom(node.Object.Type.GetTypeInfo())) - { - return false; - } - return node.Method.IsGenericMethod && node.Method.GetGenericMethodDefinition() == getMethod; - } - - - /// - /// Visits an Expression, converting AVObject.Get/AVObject[]/AVObject.Property, - /// and nested indices into a single call to AVObject.Get() with a "field path" like - /// "foo.bar.baz" - /// - private class ObjectNormalizer : ExpressionVisitor - { - protected override Expression VisitIndex(IndexExpression node) - { - var visitedObject = Visit(node.Object); - var indexer = visitedObject as MethodCallExpression; - if (IsAVObjectGet(indexer)) - { - var indexValue = GetValue(node.Arguments[0]) as string; - if (indexValue == null) - { - throw new InvalidOperationException("Index must be a string"); - } - var newPath = GetValue(indexer.Arguments[0]) + "." + indexValue; - return Expression.Call(indexer.Object, - getMethod.MakeGenericMethod(node.Type), - Expression.Constant(newPath, typeof(string))); - } - return base.VisitIndex(node); - } - - /// - /// Check for a AVFieldName attribute and use that as the path component, turning - /// properties like foo.ObjectId into foo.Get("objectId") - /// - protected override Expression VisitMember(MemberExpression node) - { - var fieldName = node.Member.GetCustomAttribute(); - if (fieldName != null && - typeof(AVObject).GetTypeInfo().IsAssignableFrom(node.Expression.Type.GetTypeInfo())) - { - var newPath = fieldName.FieldName; - return Expression.Call(node.Expression, - getMethod.MakeGenericMethod(node.Type), - Expression.Constant(newPath, typeof(string))); - } - return base.VisitMember(node); - } - - /// - /// If a AVObject.Get() call has been cast, just change the generic parameter. - /// - protected override Expression VisitUnary(UnaryExpression node) - { - var methodCall = Visit(node.Operand) as MethodCallExpression; - if ((node.NodeType == ExpressionType.Convert || - node.NodeType == ExpressionType.ConvertChecked) && - IsAVObjectGet(methodCall)) - { - return Expression.Call(methodCall.Object, - getMethod.MakeGenericMethod(node.Type), - methodCall.Arguments); - } - return base.VisitUnary(node); - } - - protected override Expression VisitMethodCall(MethodCallExpression node) - { - if (node.Method.Name == "get_Item" && node.Object is ParameterExpression) - { - var indexPath = GetValue(node.Arguments[0]) as string; - return Expression.Call(node.Object, - getMethod.MakeGenericMethod(typeof(object)), - Expression.Constant(indexPath, typeof(string))); - } - - if (node.Method.Name == "get_Item" || IsAVObjectGet(node)) - { - var visitedObject = Visit(node.Object); - var indexer = visitedObject as MethodCallExpression; - if (IsAVObjectGet(indexer)) - { - var indexValue = GetValue(node.Arguments[0]) as string; - if (indexValue == null) - { - throw new InvalidOperationException("Index must be a string"); - } - var newPath = GetValue(indexer.Arguments[0]) + "." + indexValue; - return Expression.Call(indexer.Object, - getMethod.MakeGenericMethod(node.Type), - Expression.Constant(newPath, typeof(string))); - } - } - return base.VisitMethodCall(node); - } - } - - /// - /// Normalizes Where expressions. - /// - private class WhereNormalizer : ExpressionVisitor - { - - /// - /// Normalizes binary operators. <, >, <=, >= !=, and == - /// This puts the AVObject.Get() on the left side of the operation - /// (reversing it if necessary), and normalizes the AVObject.Get() - /// - protected override Expression VisitBinary(BinaryExpression node) - { - var leftTransformed = new ObjectNormalizer().Visit(node.Left) as MethodCallExpression; - var rightTransformed = new ObjectNormalizer().Visit(node.Right) as MethodCallExpression; - - MethodCallExpression objectExpression; - Expression filterExpression; - bool inverted; - if (leftTransformed != null) - { - objectExpression = leftTransformed; - filterExpression = node.Right; - inverted = false; - } - else - { - objectExpression = rightTransformed; - filterExpression = node.Left; - inverted = true; - } - - try - { - switch (node.NodeType) - { - case ExpressionType.GreaterThan: - if (inverted) - { - return Expression.LessThan(objectExpression, filterExpression); - } - else - { - return Expression.GreaterThan(objectExpression, filterExpression); - } - case ExpressionType.GreaterThanOrEqual: - if (inverted) - { - return Expression.LessThanOrEqual(objectExpression, filterExpression); - } - else - { - return Expression.GreaterThanOrEqual(objectExpression, filterExpression); - } - case ExpressionType.LessThan: - if (inverted) - { - return Expression.GreaterThan(objectExpression, filterExpression); - } - else - { - return Expression.LessThan(objectExpression, filterExpression); - } - case ExpressionType.LessThanOrEqual: - if (inverted) - { - return Expression.GreaterThanOrEqual(objectExpression, filterExpression); - } - else - { - return Expression.LessThanOrEqual(objectExpression, filterExpression); - } - case ExpressionType.Equal: - return Expression.Equal(objectExpression, filterExpression); - case ExpressionType.NotEqual: - return Expression.NotEqual(objectExpression, filterExpression); - } - } - catch (ArgumentException) - { - throw new InvalidOperationException("Operation not supported: " + node); - } - return base.VisitBinary(node); - } - - /// - /// If a ! operator is used, this removes the ! and instead calls the equivalent - /// function (so e.g. == becomes !=, < becomes >=, Contains becomes NotContains) - /// - protected override Expression VisitUnary(UnaryExpression node) - { - // Normalizes inversion - if (node.NodeType == ExpressionType.Not) - { - var visitedOperand = Visit(node.Operand); - var binaryOperand = visitedOperand as BinaryExpression; - if (binaryOperand != null) - { - switch (binaryOperand.NodeType) - { - case ExpressionType.GreaterThan: - return Expression.LessThanOrEqual(binaryOperand.Left, binaryOperand.Right); - case ExpressionType.GreaterThanOrEqual: - return Expression.LessThan(binaryOperand.Left, binaryOperand.Right); - case ExpressionType.LessThan: - return Expression.GreaterThanOrEqual(binaryOperand.Left, binaryOperand.Right); - case ExpressionType.LessThanOrEqual: - return Expression.GreaterThan(binaryOperand.Left, binaryOperand.Right); - case ExpressionType.Equal: - return Expression.NotEqual(binaryOperand.Left, binaryOperand.Right); - case ExpressionType.NotEqual: - return Expression.Equal(binaryOperand.Left, binaryOperand.Right); - } - } - - var methodCallOperand = visitedOperand as MethodCallExpression; - if (methodCallOperand != null) - { - if (methodCallOperand.Method.IsGenericMethod) - { - if (methodCallOperand.Method.GetGenericMethodDefinition() == containsMethod) - { - var genericNotContains = notContainsMethod.MakeGenericMethod( - methodCallOperand.Method.GetGenericArguments()); - return Expression.Call(genericNotContains, methodCallOperand.Arguments.ToArray()); - } - if (methodCallOperand.Method.GetGenericMethodDefinition() == notContainsMethod) - { - var genericContains = containsMethod.MakeGenericMethod( - methodCallOperand.Method.GetGenericArguments()); - return Expression.Call(genericContains, methodCallOperand.Arguments.ToArray()); - } - } - if (methodCallOperand.Method == containsKeyMethod) - { - return Expression.Call(notContainsKeyMethod, methodCallOperand.Arguments.ToArray()); - } - if (methodCallOperand.Method == notContainsKeyMethod) - { - return Expression.Call(containsKeyMethod, methodCallOperand.Arguments.ToArray()); - } - } - } - return base.VisitUnary(node); - } - - /// - /// Normalizes .Equals into == and Contains() into the appropriate stub. - /// - protected override Expression VisitMethodCall(MethodCallExpression node) - { - // Convert .Equals() into == - if (node.Method.Name == "Equals" && - node.Method.ReturnType == typeof(bool) && - node.Method.GetParameters().Length == 1) - { - var obj = new ObjectNormalizer().Visit(node.Object) as MethodCallExpression; - var parameter = new ObjectNormalizer().Visit(node.Arguments[0]) as MethodCallExpression; - if ((IsAVObjectGet(obj) && (obj.Object is ParameterExpression)) || - (IsAVObjectGet(parameter) && (parameter.Object is ParameterExpression))) - { - return Expression.Equal(node.Object, node.Arguments[0]); - } - } - - // Convert the .Contains() into a ContainsStub - if (node.Method != stringContains && - node.Method.Name == "Contains" && - node.Method.ReturnType == typeof(bool) && - node.Method.GetParameters().Length <= 2) - { - var collection = node.Method.GetParameters().Length == 1 ? - node.Object : - node.Arguments[0]; - var parameterIndex = node.Method.GetParameters().Length - 1; - var parameter = new ObjectNormalizer().Visit(node.Arguments[parameterIndex]) - as MethodCallExpression; - if (IsAVObjectGet(parameter) && (parameter.Object is ParameterExpression)) - { - var genericContains = containsMethod.MakeGenericMethod(parameter.Type); - return Expression.Call(genericContains, collection, parameter); - } - var target = new ObjectNormalizer().Visit(collection) as MethodCallExpression; - var element = node.Arguments[parameterIndex]; - if (IsAVObjectGet(target) && (target.Object is ParameterExpression)) - { - var genericContains = containsMethod.MakeGenericMethod(element.Type); - return Expression.Call(genericContains, target, element); - } - } - - // Convert obj["foo.bar"].ContainsKey("baz") into obj.ContainsKey("foo.bar.baz") - if (node.Method.Name == "ContainsKey" && - node.Method.ReturnType == typeof(bool) && - node.Method.GetParameters().Length == 1) - { - var getter = new ObjectNormalizer().Visit(node.Object) as MethodCallExpression; - Expression target = null; - string path = null; - if (IsAVObjectGet(getter) && getter.Object is ParameterExpression) - { - target = getter.Object; - path = GetValue(getter.Arguments[0]) + "." + GetValue(node.Arguments[0]); - return Expression.Call(containsKeyMethod, target, Expression.Constant(path)); - } - else if (node.Object is ParameterExpression) - { - target = node.Object; - path = GetValue(node.Arguments[0]) as string; - } - if (target != null && path != null) - { - return Expression.Call(containsKeyMethod, target, Expression.Constant(path)); - } - } - return base.VisitMethodCall(node); - } - } - - /// - /// Converts a normalized method call expression into the appropriate AVQuery clause. - /// - private static AVQuery WhereMethodCall( - this AVQuery source, Expression> expression, MethodCallExpression node) - where T : AVObject - { - if (IsAVObjectGet(node) && (node.Type == typeof(bool) || node.Type == typeof(bool?))) - { - // This is a raw boolean field access like 'where obj.Get("foo")' - return source.WhereEqualTo(GetValue(node.Arguments[0]) as string, true); - } - - MethodInfo translatedMethod; - if (functionMappings.TryGetValue(node.Method, out translatedMethod)) - { - var objTransformed = new ObjectNormalizer().Visit(node.Object) as MethodCallExpression; - if (!(IsAVObjectGet(objTransformed) && - objTransformed.Object == expression.Parameters[0])) - { - throw new InvalidOperationException( - "The left-hand side of a supported function call must be a AVObject field access."); - } - var fieldPath = GetValue(objTransformed.Arguments[0]); - var containedIn = GetValue(node.Arguments[0]); - var queryType = translatedMethod.DeclaringType.GetGenericTypeDefinition() - .MakeGenericType(typeof(T)); - translatedMethod = ReflectionHelpers.GetMethod(queryType, - translatedMethod.Name, - translatedMethod.GetParameters().Select(p => p.ParameterType).ToArray()); - return translatedMethod.Invoke(source, new[] { fieldPath, containedIn }) as AVQuery; - } - - if (node.Arguments[0] == expression.Parameters[0]) - { - // obj.ContainsKey("foo") --> query.WhereExists("foo") - if (node.Method == containsKeyMethod) - { - return source.WhereExists(GetValue(node.Arguments[1]) as string); - } - // !obj.ContainsKey("foo") --> query.WhereDoesNotExist("foo") - if (node.Method == notContainsKeyMethod) - { - return source.WhereDoesNotExist(GetValue(node.Arguments[1]) as string); - } - } - - if (node.Method.IsGenericMethod) - { - if (node.Method.GetGenericMethodDefinition() == containsMethod) - { - // obj.Get>("path").Contains(someValue) - if (IsAVObjectGet(node.Arguments[0] as MethodCallExpression)) - { - return source.WhereEqualTo( - GetValue(((MethodCallExpression)node.Arguments[0]).Arguments[0]) as string, - GetValue(node.Arguments[1])); - } - // someList.Contains(obj.Get("path")) - if (IsAVObjectGet(node.Arguments[1] as MethodCallExpression)) - { - var collection = GetValue(node.Arguments[0]) as System.Collections.IEnumerable; - return source.WhereContainedIn( - GetValue(((MethodCallExpression)node.Arguments[1]).Arguments[0]) as string, - collection.Cast()); - } - } - - if (node.Method.GetGenericMethodDefinition() == notContainsMethod) - { - // !obj.Get>("path").Contains(someValue) - if (IsAVObjectGet(node.Arguments[0] as MethodCallExpression)) - { - return source.WhereNotEqualTo( - GetValue(((MethodCallExpression)node.Arguments[0]).Arguments[0]) as string, - GetValue(node.Arguments[1])); - } - // !someList.Contains(obj.Get("path")) - if (IsAVObjectGet(node.Arguments[1] as MethodCallExpression)) - { - var collection = GetValue(node.Arguments[0]) as System.Collections.IEnumerable; - return source.WhereNotContainedIn( - GetValue(((MethodCallExpression)node.Arguments[1]).Arguments[0]) as string, - collection.Cast()); - } - } - } - throw new InvalidOperationException(node.Method + " is not a supported method call in a where expression."); - } - - /// - /// Converts a normalized binary expression into the appropriate AVQuery clause. - /// - private static AVQuery WhereBinaryExpression( - this AVQuery source, Expression> expression, BinaryExpression node) - where T : AVObject - { - var leftTransformed = new ObjectNormalizer().Visit(node.Left) as MethodCallExpression; - - if (!(IsAVObjectGet(leftTransformed) && - leftTransformed.Object == expression.Parameters[0])) - { - throw new InvalidOperationException( - "Where expressions must have one side be a field operation on a AVObject."); - } - - var fieldPath = GetValue(leftTransformed.Arguments[0]) as string; - var filterValue = GetValue(node.Right); - - switch (node.NodeType) - { - case ExpressionType.GreaterThan: - return source.WhereGreaterThan(fieldPath, filterValue); - case ExpressionType.GreaterThanOrEqual: - return source.WhereGreaterThanOrEqualTo(fieldPath, filterValue); - case ExpressionType.LessThan: - return source.WhereLessThan(fieldPath, filterValue); - case ExpressionType.LessThanOrEqual: - return source.WhereLessThanOrEqualTo(fieldPath, filterValue); - case ExpressionType.Equal: - return source.WhereEqualTo(fieldPath, filterValue); - case ExpressionType.NotEqual: - return source.WhereNotEqualTo(fieldPath, filterValue); - default: - throw new InvalidOperationException( - "Where expressions do not support this operator."); - } - } - - /// - /// Filters a query based upon the predicate provided. - /// - /// The type of AVObject being queried for. - /// The base to which - /// the predicate will be added. - /// A function to test each AVObject for a condition. - /// The predicate must be able to be represented by one of the standard Where - /// functions on AVQuery - /// A new AVQuery whose results will match the given predicate as - /// well as the Source's filters. - public static AVQuery Where( - this AVQuery source, Expression> predicate) - where TSource : AVObject - { - // Handle top-level logic operators && and || - var binaryExpression = predicate.Body as BinaryExpression; - if (binaryExpression != null) - { - if (binaryExpression.NodeType == ExpressionType.AndAlso) - { - return source - .Where(Expression.Lambda>( - binaryExpression.Left, predicate.Parameters)) - .Where(Expression.Lambda>( - binaryExpression.Right, predicate.Parameters)); - } - if (binaryExpression.NodeType == ExpressionType.OrElse) - { - var left = source.Where(Expression.Lambda>( - binaryExpression.Left, predicate.Parameters)); - var right = source.Where(Expression.Lambda>( - binaryExpression.Right, predicate.Parameters)); - return left.Or(right); - } - } - - var normalized = new WhereNormalizer().Visit(predicate.Body); - - var methodCallExpr = normalized as MethodCallExpression; - if (methodCallExpr != null) - { - return source.WhereMethodCall(predicate, methodCallExpr); - } - - var binaryExpr = normalized as BinaryExpression; - if (binaryExpr != null) - { - return source.WhereBinaryExpression(predicate, binaryExpr); - } - - var unaryExpr = normalized as UnaryExpression; - if (unaryExpr != null && unaryExpr.NodeType == ExpressionType.Not) - { - var node = unaryExpr.Operand as MethodCallExpression; - if (IsAVObjectGet(node) && (node.Type == typeof(bool) || node.Type == typeof(bool?))) - { - // This is a raw boolean field access like 'where !obj.Get("foo")' - return source.WhereNotEqualTo(GetValue(node.Arguments[0]) as string, true); - } - } - - throw new InvalidOperationException( - "Encountered an unsupported expression for AVQueries."); - } - - /// - /// Normalizes an OrderBy's keySelector expression and then extracts the path - /// from the AVObject.Get() call. - /// - private static string GetOrderByPath( - Expression> keySelector) - { - string result = null; - var normalized = new ObjectNormalizer().Visit(keySelector.Body); - var callExpr = normalized as MethodCallExpression; - if (IsAVObjectGet(callExpr) && callExpr.Object == keySelector.Parameters[0]) - { - // We're operating on the parameter - result = GetValue(callExpr.Arguments[0]) as string; - } - if (result == null) - { - throw new InvalidOperationException( - "OrderBy expression must be a field access on a AVObject."); - } - return result; - } - - /// - /// Orders a query based upon the key selector provided. - /// - /// The type of AVObject being queried for. - /// The type of key returned by keySelector. - /// The query to order. - /// A function to extract a key from the AVObject. - /// A new AVQuery based on Source whose results will be ordered by - /// the key specified in the keySelector. - public static AVQuery OrderBy( - this AVQuery source, Expression> keySelector) - where TSource : AVObject - { - return source.OrderBy(GetOrderByPath(keySelector)); - } - - /// - /// Orders a query based upon the key selector provided. - /// - /// The type of AVObject being queried for. - /// The type of key returned by keySelector. - /// The query to order. - /// A function to extract a key from the AVObject. - /// A new AVQuery based on Source whose results will be ordered by - /// the key specified in the keySelector. - public static AVQuery OrderByDescending( - this AVQuery source, Expression> keySelector) - where TSource : AVObject - { - return source.OrderByDescending(GetOrderByPath(keySelector)); - } - - /// - /// Correlates the elements of two queries based on matching keys. - /// - /// The type of AVObjects of the first query. - /// The type of AVObjects of the second query. - /// The type of the keys returned by the key selector - /// functions. - /// The type of the result. This must match either - /// TOuter or TInner - /// The first query to join. - /// The query to join to the first query. - /// A function to extract a join key from the results of - /// the first query. - /// A function to extract a join key from the results of - /// the second query. - /// A function to select either the outer or inner query - /// result to determine which query is the base query. - /// A new AVQuery with a WhereMatchesQuery or WhereMatchesKeyInQuery - /// clause based upon the query indicated in the . - public static AVQuery Join( - this AVQuery outer, - AVQuery inner, - Expression> outerKeySelector, - Expression> innerKeySelector, - Expression> resultSelector) - where TOuter : AVObject - where TInner : AVObject - where TResult : AVObject - { - // resultSelector must select either the inner object or the outer object. If it's the inner - // object, reverse the query. - if (resultSelector.Body == resultSelector.Parameters[1]) - { - // The inner object was selected. - return inner.Join( - outer, - innerKeySelector, - outerKeySelector, - (i, o) => i) as AVQuery; - } - if (resultSelector.Body != resultSelector.Parameters[0]) - { - throw new InvalidOperationException("Joins must select either the outer or inner object."); - } - - // Normalize both selectors - Expression outerNormalized = new ObjectNormalizer().Visit(outerKeySelector.Body); - Expression innerNormalized = new ObjectNormalizer().Visit(innerKeySelector.Body); - MethodCallExpression outerAsGet = outerNormalized as MethodCallExpression; - MethodCallExpression innerAsGet = innerNormalized as MethodCallExpression; - if (IsAVObjectGet(outerAsGet) && outerAsGet.Object == outerKeySelector.Parameters[0]) - { - var outerKey = GetValue(outerAsGet.Arguments[0]) as string; - - if (IsAVObjectGet(innerAsGet) && innerAsGet.Object == innerKeySelector.Parameters[0]) - { - // Both are key accesses, so treat this as a WhereMatchesKeyInQuery - var innerKey = GetValue(innerAsGet.Arguments[0]) as string; - return outer.WhereMatchesKeyInQuery(outerKey, innerKey, inner) as AVQuery; - } - - if (innerKeySelector.Body == innerKeySelector.Parameters[0]) - { - // The inner selector is on the result of the query itself, so treat this as a - // WhereMatchesQuery - return outer.WhereMatchesQuery(outerKey, inner) as AVQuery; - } - throw new InvalidOperationException( - "The key for the joined object must be a AVObject or a field access " + - "on the AVObject."); - } - - // TODO (hallucinogen): If we ever support "and" queries fully and/or support a "where this object - // matches some key in some other query" (as opposed to requiring a key on this query), we - // can add support for even more types of joins. - - throw new InvalidOperationException( - "The key for the selected object must be a field access on the AVObject."); - } - } -} diff --git a/Storage/Storage/Public/AVRelation.cs b/Storage/Storage/Public/AVRelation.cs deleted file mode 100644 index 1be6f4b..0000000 --- a/Storage/Storage/Public/AVRelation.cs +++ /dev/null @@ -1,147 +0,0 @@ -using LeanCloud.Storage.Internal; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics; -using System.Linq.Expressions; - -namespace LeanCloud { - /// - /// A common base class for AVRelations. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public abstract class AVRelationBase : IJsonConvertible { - private AVObject parent; - private string key; - private string targetClassName; - - internal AVRelationBase(AVObject parent, string key) { - EnsureParentAndKey(parent, key); - } - - internal AVRelationBase(AVObject parent, string key, string targetClassName) - : this(parent, key) { - this.targetClassName = targetClassName; - } - - internal static ObjectSubclassingController SubclassingController { - get { - return AVPlugins.Instance.SubclassingController; - } - } - - internal void EnsureParentAndKey(AVObject parent, string key) { - this.parent = this.parent ?? parent; - this.key = this.key ?? key; - Debug.Assert(this.parent == parent, "Relation retrieved from two different objects"); - Debug.Assert(this.key == key, "Relation retrieved from two different keys"); - } - - internal void Add(AVObject obj) { - var change = new AVRelationOperation(new[] { obj }, null); - parent.PerformOperation(key, change); - targetClassName = change.TargetClassName; - } - - internal void Remove(AVObject obj) { - var change = new AVRelationOperation(null, new[] { obj }); - parent.PerformOperation(key, change); - targetClassName = change.TargetClassName; - } - - IDictionary IJsonConvertible.ToJSON() { - return new Dictionary { - { "__type", "Relation"}, - { "className", targetClassName} - }; - } - - internal AVQuery GetQuery() where T : AVObject { - if (targetClassName != null) { - return new AVQuery(targetClassName) - .WhereRelatedTo(parent, key); - } - - return new AVQuery(parent.ClassName) - .WhereRelatedTo(parent, key); - } - - internal AVQuery GetReverseQuery(T target) where T : AVObject { - if (target.ObjectId == null) { - throw new ArgumentNullException(nameof(target), "can not query a relation without target ObjectId."); - } - - return new AVQuery(parent.ClassName).WhereEqualTo(key, target); - } - - internal string TargetClassName { - get { - return targetClassName; - } - set { - targetClassName = value; - } - } - - /// - /// Produces the proper AVRelation<T> instance for the given classname. - /// - internal static AVRelationBase CreateRelation(AVObject parent, - string key, - string targetClassName) { - var targetType = SubclassingController.GetType(targetClassName) ?? typeof(AVObject); - - Expression>> createRelationExpr = - () => CreateRelation(parent, key, targetClassName); - var createRelationMethod = - ((MethodCallExpression)createRelationExpr.Body) - .Method - .GetGenericMethodDefinition() - .MakeGenericMethod(targetType); - return (AVRelationBase)createRelationMethod.Invoke(null, new object[] { parent, key, targetClassName }); - } - - private static AVRelation CreateRelation(AVObject parent, string key, string targetClassName) - where T : AVObject { - return new AVRelation(parent, key, targetClassName); - } - } - - /// - /// Provides access to all of the children of a many-to-many relationship. Each instance of - /// AVRelation is associated with a particular parent and key. - /// - /// The type of the child objects. - public sealed class AVRelation : AVRelationBase where T : AVObject { - - internal AVRelation(AVObject parent, string key) : base(parent, key) { } - - internal AVRelation(AVObject parent, string key, string targetClassName) - : base(parent, key, targetClassName) { } - - /// - /// Adds an object to this relation. The object must already have been saved. - /// - /// The object to add. - public void Add(T obj) { - base.Add(obj); - } - - /// - /// Removes an object from this relation. The object must already have been saved. - /// - /// The object to remove. - public void Remove(T obj) { - base.Remove(obj); - } - - /// - /// Gets a query that can be used to query the objects in this relation. - /// - public AVQuery Query { - get { - return base.GetQuery(); - } - } - } -} diff --git a/Storage/Storage/Public/AVRole.cs b/Storage/Storage/Public/AVRole.cs deleted file mode 100644 index df107c8..0000000 --- a/Storage/Storage/Public/AVRole.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using System.Text.RegularExpressions; - -namespace LeanCloud { - /// - /// 角色类 - /// - [AVClassName("_Role")] - public class AVRole : AVObject { - private static readonly Regex namePattern = new Regex("^[0-9a-zA-Z_\\- ]+$"); - - /// - /// Constructs a new AVRole. You must assign a name and ACL to the role. - /// - public AVRole() { } - - /// - /// Constructs a new AVRole with the given name. - /// - /// The name of the role to create. - /// The ACL for this role. Roles must have an ACL. - public AVRole(string name, AVACL acl) - : this() { - Name = name; - ACL = acl; - } - - /// - /// Gets the name of the role. - /// - [AVFieldName("name")] - public string Name { - get { - return GetProperty("Name"); - } - set { - SetProperty(value, "Name"); - } - } - - /// - /// Gets the for the s that are - /// direct children of this role. These users are granted any privileges that - /// this role has been granted (e.g. read or write access through ACLs). You can - /// add or remove child users from the role through this relation. - /// - [AVFieldName("users")] - public AVRelation Users { - get { - return GetRelationProperty("Users"); - } - } - - /// - /// Gets the for the s that are - /// direct children of this role. These roles' users are granted any privileges that - /// this role has been granted (e.g. read or write access through ACLs). You can - /// add or remove child roles from the role through this relation. - /// - [AVFieldName("roles")] - public AVRelation Roles { - get { - return GetRelationProperty("Roles"); - } - } - - internal override void OnSettingValue(ref string key, ref object value) { - base.OnSettingValue(ref key, ref value); - if (key == "name") { - if (ObjectId != null) { - throw new InvalidOperationException( - "A role's name can only be set before it has been saved."); - } - if (!(value is string)) { - throw new ArgumentException("A role's name must be a string.", nameof(value)); - } - if (!namePattern.IsMatch((string)value)) { - throw new ArgumentException( - "A role's name can only contain alphanumeric characters, _, -, and spaces.", nameof(value)); - } - } - } - - /// - /// Gets a over the Role collection. - /// - public static AVQuery Query { - get { - return new AVQuery(); - } - } - } -} diff --git a/Storage/Storage/Public/AVUploadProgressEventArgs.cs b/Storage/Storage/Public/AVUploadProgressEventArgs.cs deleted file mode 100644 index 5134fab..0000000 --- a/Storage/Storage/Public/AVUploadProgressEventArgs.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace LeanCloud { - /// - /// Represents upload progress. - /// - public class AVUploadProgressEventArgs : EventArgs { - - /// - /// Gets the progress (a number between 0.0 and 1.0) of an upload. - /// - public double Progress { get; set; } - } -} diff --git a/Storage/Storage/Public/AVUser.cs b/Storage/Storage/Public/AVUser.cs deleted file mode 100644 index 5521060..0000000 --- a/Storage/Storage/Public/AVUser.cs +++ /dev/null @@ -1,646 +0,0 @@ -using LeanCloud.Storage.Internal; -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; - -namespace LeanCloud { - /// - /// 用户类 - /// - [AVClassName("_User")] - public class AVUser : AVObject { - internal static AVUserController UserController { - get { - return AVPlugins.Instance.UserController; - } - } - - /// - /// 获取当前用户 - /// - public static AVUser CurrentUser { - get; - internal set; - } - - /// - /// 创建一个 AVUser 查询对象 - /// - public static AVQuery Query { - get { - return new AVQuery(); - } - } - - - /// - /// 用户名 - /// - [AVFieldName("username")] - public string Username { - get { - return GetProperty(null, "Username"); - } - set { - SetProperty(value, "Username"); - } - } - - /// - /// 密码 - /// - [AVFieldName("password")] - public string Password { - private get { - return GetProperty(null, "Password"); - } - set { - SetProperty(value, "Password"); - } - } - - /// - /// Email - /// - [AVFieldName("email")] - public string Email { - get { - return GetProperty(null, "Email"); - } - set { - SetProperty(value, "Email"); - } - } - - /// - /// 手机号 - /// - [AVFieldName("mobilePhoneNumber")] - public string MobilePhoneNumber { - get { - return GetProperty(null, "MobilePhoneNumber"); - } - set { - SetProperty(value, "MobilePhoneNumber"); - } - } - - /// - /// 手机号是否已经验证 - /// - /// true if mobile phone verified; otherwise, false. - [AVFieldName("mobilePhoneVerified")] - public bool MobilePhoneVerified { - get { - return GetProperty(false, "MobilePhoneVerified"); - } - set { - SetProperty(value, "MobilePhoneVerified"); - } - } - - /// - /// 获取 Session Token - /// - public string SessionToken { - get { - if (State.ContainsKey("sessionToken")) { - return State["sessionToken"] as string; - } - return null; - } - } - - /// - /// 用户数据 - /// - internal IDictionary> AuthData { - get { - if (TryGetValue("authData", out IDictionary> authData)) { - return authData; - } - return null; - } - private set { - this["authData"] = value; - } - } - - /// - /// 判断用户是否为匿名用户 - /// - public bool IsAnonymous { - get { - return AuthData != null && AuthData.Keys.Contains("anonymous"); - } - } - - /// - /// 判断是否是当前用户 - /// - public bool IsCurrent { - get { - return CurrentUser == this; - } - } - - /// - /// 判断当前用户的 Session Token 是否有效 - /// - /// - public async Task IsAuthenticatedAsync() { - if (SessionToken == null || CurrentUser == null || CurrentUser.ObjectId != ObjectId) { - return false; - } - var command = new AVCommand { - Path = $"users/me?session_token={SessionToken}", - Method = HttpMethod.Get - }; - await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command); - return true; - } - - /// - /// 刷新用户的 Session Token,刷新后 Session Token 将会改变 - /// - /// - public async Task RefreshSessionTokenAsync() { - var serverState = await UserController.RefreshSessionTokenAsync(ObjectId); - HandleSave(serverState); - } - - #region 账号密码登陆 - - /// - /// 注册新用户,注册成功后将保存为当前用户。必须设置用户名和密码。 - /// - /// - public async Task SignUpAsync() { - if (AuthData == null) { - if (string.IsNullOrEmpty(Username)) { - throw new InvalidOperationException("Cannot sign up user with an empty name."); - } - if (string.IsNullOrEmpty(Password)) { - throw new InvalidOperationException("Cannot sign up user with an empty password."); - } - } - if (!string.IsNullOrEmpty(ObjectId)) { - throw new InvalidOperationException("Cannot sign up a user that already exists."); - } - - var serverState = await UserController.SignUpAsync(State, operationDict); - HandleSave(serverState); - CurrentUser = this; - } - - /// - /// 使用用户名和密码登陆。登陆成功后,将用户设置为当前用户 - /// - /// 用户名 - /// 密码 - /// - public static async Task LogInAsync(string username, string password) { - var ret = await UserController.LogInAsync(username, null, password); - AVUser user = FromState(ret, "_User"); - CurrentUser = user; - return user; - } - - /// - /// 用邮箱作和密码匹配登录 - /// - /// 邮箱 - /// 密码 - /// - public static async Task LogInWithEmailAsync(string email, string password) { - var ret = await UserController.LogInAsync(null, email, password); - AVUser user = FromState(ret, "_User"); - CurrentUser = user; - return CurrentUser; - } - - /// - /// 通过绑定的邮箱请求重置密码 - /// 邮件可以在 LeanCloud 站点安全的重置密码 - /// - /// 绑定的邮箱地址 - /// - public static Task RequestPasswordResetAsync(string email) { - return UserController.RequestPasswordResetAsync(email); - } - - /// - /// 更新用户的密码,需要用户的旧密码 - /// - /// 旧密码 - /// 新密码 - public async Task UpdatePasswordAsync(string oldPassword, string newPassword) { - IObjectState state = await UserController.UpdatePasswordAsync(ObjectId, oldPassword, newPassword); - HandleFetchResult(state); - } - - /// - /// 申请发送验证邮箱的邮件,一周之内有效 - /// 如果该邮箱已经验证通过,会直接返回 True,并不会真正发送邮件 - /// 注意,不能频繁的调用此接口,一天之内只允许向同一个邮箱发送验证邮件 3 次,超过调用次数,会直接返回错误 - /// - /// 邮箱地址 - /// - public static Task RequestEmailVerifyAsync(string email) { - Dictionary strs = new Dictionary { - { "email", email } - }; - var command = new AVCommand { - Path = "requestEmailVerify", - Method = HttpMethod.Post, - Content = strs - }; - return AVPlugins.Instance.CommandRunner.RunCommandAsync>(command); - } - - #endregion - - #region 手机号登录 - - /// - /// 用手机号和密码匹配登陆 - /// - /// 手机号 - /// 密码 - /// - public static Task LogInWithMobilePhoneNumberAsync(string mobilePhoneNumber, string password) { - Dictionary strs = new Dictionary { - { "mobilePhoneNumber", mobilePhoneNumber }, - { "password", password } - }; - return LogInWithParametersAsync(strs); - } - - /// - /// 用手机号和验证码登陆 - /// - /// 手机号 - /// 短信验证码 - /// - public static Task LogInWithMobilePhoneNumberSmsCodeAsync(string mobilePhoneNumber, string smsCode) { - Dictionary strs = new Dictionary { - { "mobilePhoneNumber", mobilePhoneNumber }, - { "smsCode", smsCode } - }; - return LogInWithParametersAsync(strs); - } - - /// - /// 请求登录短信验证码 - /// - /// 手机号 - /// 验证码 - /// - public static Task RequestLogInSmsCodeAsync(string mobilePhoneNumber, string validateToken) { - Dictionary strs = new Dictionary { - { "mobilePhoneNumber", mobilePhoneNumber }, - }; - if (string.IsNullOrEmpty(validateToken)) { - strs.Add("validate_token", validateToken); - } - var command = new AVCommand { - Path = "requestLoginSmsCode", - Method = HttpMethod.Post, - Content = strs - }; - return AVPlugins.Instance.CommandRunner.RunCommandAsync>(command); - } - - /// - /// 手机号注册和登录 - /// - /// 手机号 - /// 短信验证码 - /// - public static async Task SignUpOrLogInByMobilePhoneAsync(string mobilePhoneNumber, string smsCode) { - Dictionary strs = new Dictionary { - { "mobilePhoneNumber", mobilePhoneNumber }, - { "smsCode", smsCode } - }; - var ret = await UserController.LogInWithParametersAsync("usersByMobilePhone", strs); - var user = CreateWithoutData(null); - user.HandleFetchResult(ret); - CurrentUser = user; - return CurrentUser; - } - - #endregion - - #region 第三方登录 - - /// - /// 使用第三方数据注册;如果已经存在相同的 Auth Data,则执行登录 - /// - /// Auth Data - /// 平台 - /// - /// - public static Task LogInWithAuthDataAsync(IDictionary authData, string platform, AVUserAuthDataLogInOption options = null) { - if (options == null) { - options = new AVUserAuthDataLogInOption(); - } - return LogInWithAsync(platform, authData, options.FailOnNotExist); - } - - /// - /// 使用第三方数据注册; - /// - /// Auth data - /// 平台标识 - /// - /// - /// - public static Task LogInWithAuthDataAndUnionIdAsync(IDictionary authData, string platform, string unionId, - AVUserAuthDataLogInOption options = null) { - if (options == null) { - options = new AVUserAuthDataLogInOption(); - } - MergeAuthData(authData, unionId, options); - return LogInWithAsync(platform, authData, options.FailOnNotExist); - } - - /// - /// 绑定第三方登录 - /// - /// Auth data - /// 平台标识 - /// - public Task AssociateAuthDataAsync(IDictionary authData, string platform) { - return LinkWithAuthDataAsync(platform, authData); - } - - /// - /// 绑定第三方登录 - /// - /// Auth data - /// 平台标识 - /// - /// - /// - public Task AssociateAuthDataAndUnionIdAsync(IDictionary authData, string platform, string unionId, - AVUserAuthDataLogInOption options = null) { - if (options == null) { - options = new AVUserAuthDataLogInOption(); - } - MergeAuthData(authData, unionId, options); - return LinkWithAuthDataAsync(platform, authData); - } - - /// - /// 解绑第三方登录 - /// - /// 平台标识 - /// - public Task DisassociateWithAuthDataAsync(string platform) { - return LinkWithAuthDataAsync(platform, null); - } - - #endregion - - #region 重置密码 - - /// - /// 请求重置密码,需要传入注册时使用的手机号。 - /// - /// 注册时使用的手机号 - /// 图形验证码 - /// - public static Task RequestPasswordResetBySmsCode(string mobilePhoneNumber, string validateToken = null) { - Dictionary strs = new Dictionary { - { "mobilePhoneNumber", mobilePhoneNumber }, - }; - if (string.IsNullOrEmpty(validateToken)) { - strs.Add("validate_token", validateToken); - } - var command = new AVCommand { - Path = "requestPasswordResetBySmsCode", - Method = HttpMethod.Post, - Content = strs - }; - return AVPlugins.Instance.CommandRunner.RunCommandAsync>(command); - } - - /// - /// 通过验证码重置密码。 - /// - /// 新密码 - /// 6 位数验证码 - /// - public static Task ResetPasswordBySmsCodeAsync(string newPassword, string smsCode) { - Dictionary strs = new Dictionary { - { "password", newPassword } - }; - var command = new AVCommand { - Path = $"resetPasswordBySmsCode/{smsCode}", - Method = HttpMethod.Put, - Content = strs - }; - return AVPlugins.Instance.CommandRunner.RunCommandAsync>(command); - } - - /// - /// 发送验证码到用户绑定的手机上 - /// - /// 手机号 - /// 验证码 - /// - public static Task RequestMobilePhoneVerifyAsync(string mobilePhoneNumber, string validateToken = null) { - Dictionary strs = new Dictionary { - { "mobilePhoneNumber", mobilePhoneNumber } - }; - if (!string.IsNullOrEmpty(validateToken)) { - strs.Add("validate_token", validateToken); - } - var command = new AVCommand { - Path = "requestMobilePhoneVerify", - Method = HttpMethod.Post, - Content = strs - }; - return AVPlugins.Instance.CommandRunner.RunCommandAsync>(command); - } - - /// - /// 验证手机验证码是否为有效值 - /// - /// 手机收到的验证码 - /// - public static Task VerifyMobilePhoneAsync(string code) { - var command = new AVCommand { - Path = $"verifyMobilePhone/{code.Trim()}", - Method = HttpMethod.Post - }; - return AVPlugins.Instance.CommandRunner.RunCommandAsync>(command); - } - - #endregion - - /// - /// 匿名登录 - /// - /// - public static Task LogInAnonymouslyAsync() { - var data = new Dictionary { - { "id", Guid.NewGuid().ToString() } - }; - var options = new AVUserAuthDataLogInOption(); - return LogInWithAuthDataAsync(data, "anonymous", options); - } - - /// - /// 使用 Session Token 登录。 - /// - /// Session Token - /// - public static async Task BecomeAsync(string sessionToken) { - var ret = await UserController.GetUserAsync(sessionToken); - AVUser user = FromState(ret, "_User"); - CurrentUser = user; - return user; - } - - /// - /// 用户登出 - /// - public static void LogOut() { - CurrentUser = null; - } - - #region 事件流系统相关 API - - /// - /// 关注某个用户 - /// - /// 被关注的用户 - /// - public Task FollowAsync(string userObjectId) { - return FollowAsync(userObjectId, null); - } - - /// - /// 关注某个用户 - /// - /// 被关注的用户 Id - /// 关注的时候附加属性 - /// - public Task FollowAsync(string userObjectId, IDictionary data) { - if (data != null) { - data = EncodeForSaving(data); - } - var command = new AVCommand { - Path = $"users/{ObjectId}/friendship/{userObjectId}", - Method = HttpMethod.Post, - Content = data - }; - return AVPlugins.Instance.CommandRunner.RunCommandAsync>(command); - } - - /// - /// 取关某一个用户 - /// - /// 用户 Id - /// - public Task UnfollowAsync(string userObjectId) { - var command = new AVCommand { - Path = $"users/{ObjectId}/friendship/{userObjectId}", - Method = HttpMethod.Delete - }; - return AVPlugins.Instance.CommandRunner.RunCommandAsync>(command); - } - - /// - /// 获取当前用户的关注者的查询 - /// - /// - public AVQuery GetFollowerQuery() { - AVQuery query = new AVQuery { - Path = $"users/{ObjectId}/followers" - }; - return query; - } - - /// - /// 获取当前用户所关注的用户的查询 - /// - /// - public AVQuery GetFolloweeQuery() { - AVQuery query = new AVQuery { - Path = $"users/{ObjectId}/followees" - }; - return query; - } - - /// - /// 同时查询关注了当前用户的关注者和当前用户所关注的用户 - /// - /// - public AVQuery GetFollowersAndFolloweesQuery() { - AVQuery query = new AVQuery { - Path = $"users/{ObjectId}/followersAndFollowees" - }; - return query; - } - - /// - /// 获取当前用户的关注者 - /// - /// - public Task> GetFollowersAsync() { - return GetFollowerQuery().FindAsync(CancellationToken.None); - } - - /// - /// 获取当前用户所关注的用户 - /// - /// - public Task> GetFolloweesAsync() { - return GetFolloweeQuery().FindAsync(CancellationToken.None); - } - - #endregion - - public Task> GetRolesAsync() { - AVQuery query = new AVQuery(); - query.WhereEqualTo("users", this); - return query.FindAsync(); - } - - Task LinkWithAuthDataAsync(string authType, IDictionary data) { - AuthData = new Dictionary> { - [authType] = data - }; - return SaveAsync(); - } - - internal static async Task LogInWithAsync(string authType, IDictionary data, bool failOnNotExist) { - var ret = await UserController.LogInAsync(authType, data, failOnNotExist); - AVUser user = FromState(ret, "_User"); - user.AuthData = new Dictionary> { - [authType] = data - }; - CurrentUser = user; - return CurrentUser; - } - - internal static async Task LogInWithParametersAsync(Dictionary strs) { - IObjectState ret = await UserController.LogInWithParametersAsync("login", strs); - AVUser user = CreateWithoutData(null); - user.HandleFetchResult(ret); - CurrentUser = user; - return CurrentUser; - } - - /// 合并为支持 AuthData 的格式 - static void MergeAuthData(IDictionary authData, string unionId, AVUserAuthDataLogInOption options) { - authData["platform"] = options.UnionIdPlatform; - authData["main_account"] = options.AsMainAccount; - authData["unionid"] = unionId; - } - } -} diff --git a/Storage/Storage/Public/AVUserAuthDataLogInOption.cs b/Storage/Storage/Public/AVUserAuthDataLogInOption.cs deleted file mode 100644 index 1918c2b..0000000 --- a/Storage/Storage/Public/AVUserAuthDataLogInOption.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; - -namespace LeanCloud { - /// - /// AuthData 登陆选项 - /// - public class AVUserAuthDataLogInOption { - - /// - /// unionId platform - /// - /// unionId platform. - public string UnionIdPlatform; - - /// - /// If true, the unionId will be associated with the user. - /// - /// true If true, the unionId will be associated with the user. false. - public bool AsMainAccount; - - /// - /// If true, the login request will fail when no user matches this authData exists. - /// - /// true If true, the login request will fail when no user matches this authData exists. false. - public bool FailOnNotExist; - - public AVUserAuthDataLogInOption() { - UnionIdPlatform = "weixin"; - AsMainAccount = false; - FailOnNotExist = false; - } - } -} diff --git a/Storage/Storage/Public/LeaderBoard/AVLeaderboard.cs b/Storage/Storage/Public/LeaderBoard/AVLeaderboard.cs deleted file mode 100644 index 3537846..0000000 --- a/Storage/Storage/Public/LeaderBoard/AVLeaderboard.cs +++ /dev/null @@ -1,498 +0,0 @@ -using System; -using System.Threading.Tasks; -using System.Collections.Generic; -using LeanCloud.Storage.Internal; -using System.Net.Http; - -namespace LeanCloud { - /// - /// 排行榜顺序 - /// - public enum AVLeaderboardOrder { - /// - /// 升序 - /// - ASCENDING, - /// - /// 降序 - /// - DESCENDING - } - - /// - /// 排行榜更新策略 - /// - public enum AVLeaderboardUpdateStrategy { - /// - /// 更好地 - /// - BETTER, - /// - /// 最近的 - /// - LAST, - /// - /// 总和 - /// - SUM, - } - - /// - /// 排行榜刷新频率 - /// - public enum AVLeaderboardVersionChangeInterval { - /// - /// 从不 - /// - NEVER, - /// - /// 每天 - /// - DAY, - /// - /// 每周 - /// - WEEK, - /// - /// 每月 - /// - MONTH - } - - /// - /// 排行榜类 - /// - public class AVLeaderboard { - /// - /// 成绩名字 - /// - /// The name of the statistic. - public string StatisticName { - get; private set; - } - - /// - /// 排行榜顺序 - /// - /// The order. - public AVLeaderboardOrder Order { - get; private set; - } - - /// - /// 排行榜更新策略 - /// - /// The update strategy. - public AVLeaderboardUpdateStrategy UpdateStrategy { - get; private set; - } - - /// - /// 排行榜版本更新频率 - /// - /// The version change intervak. - public AVLeaderboardVersionChangeInterval VersionChangeInterval { - get; private set; - } - - /// - /// 版本号 - /// - /// The version. - public int Version { - get; private set; - } - - /// - /// 下次重置时间 - /// - /// The next reset at. - public DateTime NextResetAt { - get; private set; - } - - /// - /// 创建时间 - /// - /// The created at. - public DateTime CreatedAt { - get; private set; - } - - /// - /// Leaderboard 构造方法 - /// - /// 成绩名称 - AVLeaderboard(string statisticName) { - StatisticName = statisticName; - } - - AVLeaderboard() { - } - - /// - /// 创建排行榜对象 - /// - /// 排行榜对象 - /// 名称 - /// 排序方式 - /// 版本更新频率 - /// 成绩更新策略 - public static async Task CreateLeaderboard(string statisticName, - AVLeaderboardOrder order = AVLeaderboardOrder.DESCENDING, - AVLeaderboardUpdateStrategy updateStrategy = AVLeaderboardUpdateStrategy.BETTER, - AVLeaderboardVersionChangeInterval versionChangeInterval = AVLeaderboardVersionChangeInterval.WEEK) { - - if (string.IsNullOrEmpty(statisticName)) { - throw new ArgumentNullException(nameof(statisticName)); - } - var data = new Dictionary { - { "statisticName", statisticName }, - { "order", order.ToString().ToLower() }, - { "versionChangeInterval", versionChangeInterval.ToString().ToLower() }, - { "updateStrategy", updateStrategy.ToString().ToLower() }, - }; - var command = new AVCommand { - Path = "leaderboard/leaderboards", - Method = HttpMethod.Post, - Content = data - }; - var result = await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command); - try { - var leaderboard = Parse(result.Item2); - return leaderboard; - } catch (Exception e) { - throw new AVException(AVException.ErrorCode.InvalidJSON, e.Message); - } - } - - /// - /// 创建排行榜对象 - /// - /// 排行榜对象 - /// 名称 - public static AVLeaderboard CreateWithoutData(string statisticName) { - if (string.IsNullOrEmpty(statisticName)) { - throw new ArgumentNullException(nameof(statisticName)); - } - return new AVLeaderboard(statisticName); - } - - /// - /// 获取排行榜对象 - /// - /// 排行榜对象 - /// 名称 - public static Task GetLeaderboard(string statisticName) { - return CreateWithoutData(statisticName).Fetch(); - } - - /// - /// 更新用户成绩 - /// - /// 更新的成绩 - /// 用户 - /// 成绩 - /// 是否强行覆盖 - public static async Task> UpdateStatistics(AVUser user, Dictionary statistics, bool overwrite = false) { - if (user == null) { - throw new ArgumentNullException(nameof(user)); - } - if (statistics == null || statistics.Count == 0) { - throw new ArgumentNullException(nameof(statistics)); - } - var data = new List(); - foreach (var statistic in statistics) { - var kv = new Dictionary { - { "statisticName", statistic.Key }, - { "statisticValue", statistic.Value }, - }; - data.Add(kv); - } - var path = string.Format("leaderboard/users/{0}/statistics", user.ObjectId); - if (overwrite) { - path = string.Format("{0}?overwrite=1", path); - } - var command = new AVCommand { - Path = path, - Method = HttpMethod.Post, - Content = data - }; - var result = await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command); - try { - List statisticList = new List(); - List list = result.Item2["results"] as List; - foreach (object obj in list) { - statisticList.Add(AVStatistic.Parse(obj as IDictionary)); - } - return statisticList; - } catch (Exception e) { - throw new AVException(AVException.ErrorCode.InvalidJSON, e.Message); - } - } - - /// - /// 获取用户成绩 - /// - /// 成绩列表 - /// 用户 - /// 名称列表 - public static async Task> GetStatistics(AVUser user, List statisticNames = null) { - if (user == null) { - throw new ArgumentNullException(nameof(user)); - } - var path = string.Format("leaderboard/users/{0}/statistics", user.ObjectId); - if (statisticNames != null && statisticNames.Count > 0) { - var names = string.Join(",", statisticNames.ToArray()); - path = string.Format("{0}?statistics={1}", path, names); - } - var command = new AVCommand { - Path = path, - Method = HttpMethod.Post - }; - var result = await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command); - try { - List statisticList = new List(); - List list = result.Item2["results"] as List; - foreach (object obj in list) { - statisticList.Add(AVStatistic.Parse(obj as IDictionary)); - } - return statisticList; - } catch (Exception e) { - throw new AVException(AVException.ErrorCode.InvalidJSON, e.Message); - } - } - - /// - /// 删除用户成绩 - /// - /// 用户 - /// 名称列表 - public static async Task DeleteStatistics(AVUser user, List statisticNames) { - if (user == null) { - throw new ArgumentNullException(nameof(user)); - } - if (statisticNames == null || statisticNames.Count == 0) { - throw new ArgumentNullException(nameof(statisticNames)); - } - var path = string.Format("leaderboard/users/{0}/statistics", user.ObjectId); - var names = string.Join(",", statisticNames.ToArray()); - path = string.Format("{0}?statistics={1}", path, names); - var command = new AVCommand { - Path = path, - Method = HttpMethod.Delete, - }; - await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command); - } - - /// - /// 获取排行榜历史数据 - /// - /// 排行榜归档列表 - /// 跳过数量 - /// 分页数量 - public async Task> GetArchives(int skip = 0, int limit = 10) { - var path = string.Format("leaderboard/leaderboards/{0}/archives", StatisticName); - path = string.Format("{0}?skip={1}&limit={2}", path, skip, limit); - var command = new AVCommand { - Path = path, - Method = HttpMethod.Get - }; - var result = await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command); - List archives = new List(); - List list = result.Item2["results"] as List; - foreach (object obj in list) { - archives.Add(AVLeaderboardArchive.Parse(obj as IDictionary)); - } - return archives; - } - - /// - /// 获取排行榜结果 - /// - /// 排名列表 - public Task> GetResults(int version = -1, int skip = 0, int limit = 10, List selectUserKeys = null, - List includeStatistics = null) { - return GetResults(null, version, skip, limit, selectUserKeys, includeStatistics); - } - - /// - /// 获取用户及附近的排名 - /// - /// 排名列表 - /// 用户 - /// 版本号 - /// 跳过数量 - /// 分页数量 - /// 包含的玩家的字段列表 - /// 包含的其他排行榜名称 - public Task> GetResultsAroundUser(int version = -1, int skip = 0, int limit = 10, - List selectUserKeys = null, - List includeStatistics = null) { - return GetResults(AVUser.CurrentUser, version, skip, limit, selectUserKeys, includeStatistics); - } - - async Task> GetResults(AVUser user, - int version, int skip, int limit, - List selectUserKeys, - List includeStatistics) { - - var path = string.Format("leaderboard/leaderboards/{0}/ranks", StatisticName); - if (user != null) { - path = string.Format("{0}/{1}", path, user.ObjectId); - } - path = string.Format("{0}?skip={1}&limit={2}", path, skip, limit); - if (version != -1) { - path = string.Format("{0}&version={1}", path, version); - } - if (selectUserKeys != null) { - var keys = string.Join(",", selectUserKeys.ToArray()); - path = string.Format("{0}&includeUser={1}", path, keys); - } - if (includeStatistics != null) { - var statistics = string.Join(",", includeStatistics.ToArray()); - path = string.Format("{0}&includeStatistics={1}", path, statistics); - } - var command = new AVCommand { - Path = path, - Method = HttpMethod.Get - }; - var result = await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command); - try { - List rankingList = new List(); - List list = result.Item2["results"] as List; - foreach (object obj in list) { - rankingList.Add(AVRanking.Parse(obj as IDictionary)); - } - return rankingList; - } catch (Exception e) { - throw new AVException(AVException.ErrorCode.InvalidJSON, e.Message); - } - } - - /// - /// 设置更新策略 - /// - /// 排行榜对象 - /// 更新策略 - public async Task UpdateUpdateStrategy(AVLeaderboardUpdateStrategy updateStrategy) { - var data = new Dictionary { - { "updateStrategy", updateStrategy.ToString().ToLower() } - }; - var result = await Update(data); - UpdateStrategy = (AVLeaderboardUpdateStrategy)Enum.Parse(typeof(AVLeaderboardUpdateStrategy), result["updateStrategy"].ToString().ToUpper()); - return this; - } - - /// - /// 设置版本更新频率 - /// - /// 排行榜对象 - /// 版本更新频率 - public async Task UpdateVersionChangeInterval(AVLeaderboardVersionChangeInterval versionChangeInterval) { - var data = new Dictionary { - { "versionChangeInterval", versionChangeInterval.ToString().ToLower() } - }; - var result = await Update(data); - VersionChangeInterval = (AVLeaderboardVersionChangeInterval)Enum.Parse(typeof(AVLeaderboardVersionChangeInterval), result["versionChangeInterval"].ToString().ToUpper()); - return this; - } - - async Task> Update(Dictionary data) { - var path = string.Format("leaderboard/leaderboards/{0}", StatisticName); - var command = new AVCommand { - Path = path, - Method = HttpMethod.Put, - Content = data - }; - var result = await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command); - return result.Item2; - } - - /// - /// 拉取排行榜数据 - /// - /// 排行榜对象 - public async Task Fetch() { - var path = string.Format("leaderboard/leaderboards/{0}", StatisticName); - var command = new AVCommand { - Path = path, - Method = HttpMethod.Get - }; - var result = await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command); - try { - // 反序列化 Leaderboard 对象 - var leaderboard = Parse(result.Item2); - return leaderboard; - } catch (Exception e) { - throw new AVException(AVException.ErrorCode.InvalidJSON, e.Message); - } - } - - /// - /// 重置排行榜 - /// - /// 排行榜对象 - public async Task Reset() { - var path = string.Format("leaderboard/leaderboards/{0}/incrementVersion", StatisticName); - var command = new AVCommand { - Path = path, - Method = HttpMethod.Put - }; - var result = await AVPlugins.Instance.CommandRunner.RunCommandAsync>(command); - try { - Init(result.Item2); - return this; - } catch (Exception e) { - throw new AVException(AVException.ErrorCode.InvalidJSON, e.Message); - } - } - - /// - /// 销毁排行榜 - /// - public Task Destroy() { - var path = string.Format("leaderboard/leaderboards/{0}", StatisticName); - var command = new AVCommand { - Path = path, - Method = HttpMethod.Delete - }; - return AVPlugins.Instance.CommandRunner.RunCommandAsync>(command); - } - - static AVLeaderboard Parse(IDictionary data) { - if (data == null) { - throw new ArgumentNullException(nameof(data)); - } - var leaderboard = new AVLeaderboard(); - leaderboard.Init(data); - return leaderboard; - } - - void Init(IDictionary data) { - if (data == null) { - throw new ArgumentNullException(nameof(data)); - } - object nameObj; - if (data.TryGetValue("statisticName", out nameObj)) { - StatisticName = nameObj.ToString(); - } - object orderObj; - if (data.TryGetValue("order", out orderObj)) { - Order = (AVLeaderboardOrder)Enum.Parse(typeof(AVLeaderboardOrder), orderObj.ToString().ToUpper()); - } - object strategyObj; - if (data.TryGetValue("updateStrategy", out strategyObj)) { - UpdateStrategy = (AVLeaderboardUpdateStrategy)Enum.Parse(typeof(AVLeaderboardUpdateStrategy), strategyObj.ToString().ToUpper()); - } - object intervalObj; - if (data.TryGetValue("versionChangeInterval", out intervalObj)) { - VersionChangeInterval = (AVLeaderboardVersionChangeInterval)Enum.Parse(typeof(AVLeaderboardVersionChangeInterval), intervalObj.ToString().ToUpper()); - } - object versionObj; - if (data.TryGetValue("version", out versionObj)) { - Version = int.Parse(versionObj.ToString()); - } - } - } -} diff --git a/Storage/Storage/Public/LeaderBoard/AVLeaderboardArchive.cs b/Storage/Storage/Public/LeaderBoard/AVLeaderboardArchive.cs deleted file mode 100644 index a9df41f..0000000 --- a/Storage/Storage/Public/LeaderBoard/AVLeaderboardArchive.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.Collections.Generic; -using LeanCloud.Storage.Internal; - -namespace LeanCloud { - /// - /// 归档的排行榜 - /// - public class AVLeaderboardArchive { - /// - /// 名称 - /// - /// The name of the statistic. - public string StatisticName { - get; internal set; - } - - /// - /// 版本号 - /// - /// The version. - public int Version { - get; internal set; - } - - /// - /// 状态 - /// - /// The status. - public string Status { - get; internal set; - } - - /// - /// 下载地址 - /// - /// The URL. - public string Url { - get; internal set; - } - - /// - /// 激活时间 - /// - /// The activated at. - public DateTime ActivatedAt { - get; internal set; - } - - /// - /// 归档时间 - /// - /// The deactivated at. - public DateTime DeactivatedAt { - get; internal set; - } - - AVLeaderboardArchive() { - } - - internal static AVLeaderboardArchive Parse(IDictionary data) { - if (data == null) { - throw new ArgumentNullException(nameof(data)); - } - AVLeaderboardArchive archive = new AVLeaderboardArchive { - StatisticName = data["statisticName"].ToString(), - Version = int.Parse(data["version"].ToString()), - Status = data["status"].ToString(), - Url = data["url"].ToString(), - ActivatedAt = (DateTime)AVDecoder.Instance.Decode(data["activatedAt"]), - DeactivatedAt = (DateTime)AVDecoder.Instance.Decode(data["activatedAt"]) - }; - return archive; - } - } -} diff --git a/Storage/Storage/Public/LeaderBoard/AVRanking.cs b/Storage/Storage/Public/LeaderBoard/AVRanking.cs deleted file mode 100644 index 6d5fa90..0000000 --- a/Storage/Storage/Public/LeaderBoard/AVRanking.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using System.Collections.Generic; -using LeanCloud.Storage.Internal; - -namespace LeanCloud { - /// - /// 排名类 - /// - public class AVRanking { - /// - /// 名次 - /// - /// The rank. - public int Rank { - get; private set; - } - - /// - /// 用户 - /// - /// The user. - public AVUser User { - get; private set; - } - - public string StatisticName { - get; private set; - } - - /// - /// 分数 - /// - /// The value. - public double Value { - get; private set; - } - - /// - /// 成绩 - /// - /// The included statistics. - public List IncludedStatistics { - get; private set; - } - - AVRanking() { - } - - internal static AVRanking Parse(IDictionary data) { - if (data == null) { - throw new ArgumentNullException(nameof(data)); - } - var ranking = new AVRanking { - Rank = int.Parse(data["rank"].ToString()), - User = AVDecoder.Instance.Decode(data["user"]) as AVUser, - StatisticName = data["statisticName"].ToString(), - Value = double.Parse(data["statisticValue"].ToString()) - }; - object statisticsObj; - if (data.TryGetValue("statistics", out statisticsObj)) { - ranking.IncludedStatistics = new List(); - var statisticsObjList = statisticsObj as List; - foreach (object statisticObj in statisticsObjList) { - var statistic = AVStatistic.Parse(statisticObj as IDictionary); - ranking.IncludedStatistics.Add(statistic); - } - } - - return ranking; - } - } -} diff --git a/Storage/Storage/Public/LeaderBoard/AVStatistic.cs b/Storage/Storage/Public/LeaderBoard/AVStatistic.cs deleted file mode 100644 index 6d4fdcb..0000000 --- a/Storage/Storage/Public/LeaderBoard/AVStatistic.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace LeanCloud { - /// - /// 成绩类 - /// - public class AVStatistic { - /// - /// 排行榜名称 - /// - /// The name. - public string Name { - get; private set; - } - - /// - /// 成绩值 - /// - /// The value. - public double Value { - get; private set; - } - - /// - /// 排行榜版本 - /// - /// The version. - public int Version { - get; internal set; - } - - public AVStatistic(string name, double value) { - Name = name; - Value = value; - } - - AVStatistic() { } - - internal static AVStatistic Parse(IDictionary data) { - if (data == null) { - throw new ArgumentNullException(nameof(data)); - } - AVStatistic statistic = new AVStatistic { - Name = data["statisticName"].ToString(), - Value = double.Parse(data["statisticValue"].ToString()), - Version = int.Parse(data["version"].ToString()) - }; - return statistic; - } - } -} diff --git a/Storage/Storage/Public/Utilities/Conversion.cs b/Storage/Storage/Public/Utilities/Conversion.cs deleted file mode 100644 index a3ace3b..0000000 --- a/Storage/Storage/Public/Utilities/Conversion.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System; -using System.Collections.Generic; -using LeanCloud.Storage.Internal; - -namespace LeanCloud.Utilities -{ - /// - /// A set of utilities for converting generic types between each other. - /// - public static class Conversion - { - /// - /// Converts a value to the requested type -- coercing primitives to - /// the desired type, wrapping lists and dictionaries appropriately, - /// or else returning null. - /// - /// This should be used on any containers that might be coming from a - /// user to normalize the collection types. Collection types coming from - /// JSON deserialization can be safely assumed to be lists or dictionaries of - /// objects. - /// - public static T As(object value) where T : class - { - return ConvertTo(value) as T; - } - - /// - /// Converts a value to the requested type -- coercing primitives to - /// the desired type, wrapping lists and dictionaries appropriately, - /// or else throwing an exception. - /// - /// This should be used on any containers that might be coming from a - /// user to normalize the collection types. Collection types coming from - /// JSON deserialization can be safely assumed to be lists or dictionaries of - /// objects. - /// - public static T To(object value) - { - return (T)ConvertTo(value); - } - - /// - /// Converts a value to the requested type -- coercing primitives to - /// the desired type, wrapping lists and dictionaries appropriately, - /// or else passing the object along to the caller unchanged. - /// - /// This should be used on any containers that might be coming from a - /// user to normalize the collection types. Collection types coming from - /// JSON deserialization can be safely assumed to be lists or dictionaries of - /// objects. - /// - internal static object ConvertTo(object value) - { - if (value is T || value == null) - { - return value; - } - - if (ReflectionHelpers.IsPrimitive(typeof(T))) - { - return (T)Convert.ChangeType(value, typeof(T)); - } - - return value; - } - - /// - /// Holds a dictionary that maps a cache of interface types for related concrete types. - /// The lookup is slow the first time for each type because it has to enumerate all interface - /// on the object type, but made fast by the cache. - /// - /// The map is: - /// (object type, generic interface type) => constructed generic type - /// - private static readonly Dictionary, Type> interfaceLookupCache = - new Dictionary, Type>(); - private static Type GetInterfaceType(Type objType, Type genericInterfaceType) - { - // Side note: It so sucks to have to do this. What a piece of crap bit of code - // Unfortunately, .NET doesn't provide any of the right hooks to do this for you - // *sigh* - if (ReflectionHelpers.IsConstructedGenericType(genericInterfaceType)) - { - genericInterfaceType = genericInterfaceType.GetGenericTypeDefinition(); - } - var cacheKey = new Tuple(objType, genericInterfaceType); - if (interfaceLookupCache.ContainsKey(cacheKey)) - { - return interfaceLookupCache[cacheKey]; - } - foreach (var type in ReflectionHelpers.GetInterfaces(objType)) - { - if (ReflectionHelpers.IsConstructedGenericType(type) && - type.GetGenericTypeDefinition() == genericInterfaceType) - { - return interfaceLookupCache[cacheKey] = type; - } - } - return null; - } - } -} diff --git a/Test/Common.Test/AppRouterTest.cs b/Test/Common.Test/AppRouterTest.cs index ee9294a..acbc144 100644 --- a/Test/Common.Test/AppRouterTest.cs +++ b/Test/Common.Test/AppRouterTest.cs @@ -1,19 +1,19 @@ using System; using System.Threading.Tasks; using NUnit.Framework; -using LeanCloud; +using LeanCloud.Common; namespace Common.Test { public class AppRouterTest { - static void Print(LogLevel level, string info) { + static void Print(LeanCloud.LogLevel level, string info) { switch (level) { - case LogLevel.Debug: + case LeanCloud.LogLevel.Debug: TestContext.Out.WriteLine($"[DEBUG] {info}"); break; - case LogLevel.Warn: + case LeanCloud.LogLevel.Warn: TestContext.Out.WriteLine($"[WARNING] {info}"); break; - case LogLevel.Error: + case LeanCloud.LogLevel.Error: TestContext.Out.WriteLine($"[ERROR] {info}"); break; default: @@ -24,12 +24,12 @@ namespace Common.Test { [SetUp] public void SetUp() { - Logger.LogDelegate += Print; + LeanCloud.Logger.LogDelegate += Print; } [TearDown] public void TearDown() { - Logger.LogDelegate -= Print; + LeanCloud.Logger.LogDelegate -= Print; } [Test] diff --git a/Storage/Storage.Test/ACLTest.cs b/Test/Storage.Test/ACLTest.cs similarity index 100% rename from Storage/Storage.Test/ACLTest.cs rename to Test/Storage.Test/ACLTest.cs diff --git a/Storage/Storage.Test/CloudTest.cs b/Test/Storage.Test/CloudTest.cs similarity index 100% rename from Storage/Storage.Test/CloudTest.cs rename to Test/Storage.Test/CloudTest.cs diff --git a/Storage/Storage.Test/ExceptionTest.cs b/Test/Storage.Test/ExceptionTest.cs similarity index 100% rename from Storage/Storage.Test/ExceptionTest.cs rename to Test/Storage.Test/ExceptionTest.cs diff --git a/Storage/Storage.Test/FileTest.cs b/Test/Storage.Test/FileTest.cs similarity index 100% rename from Storage/Storage.Test/FileTest.cs rename to Test/Storage.Test/FileTest.cs diff --git a/Storage/Storage.Test/GeoTest.cs b/Test/Storage.Test/GeoTest.cs similarity index 100% rename from Storage/Storage.Test/GeoTest.cs rename to Test/Storage.Test/GeoTest.cs diff --git a/Storage/Storage.Test/ObjectTest.cs b/Test/Storage.Test/ObjectTest.cs similarity index 100% rename from Storage/Storage.Test/ObjectTest.cs rename to Test/Storage.Test/ObjectTest.cs diff --git a/Storage/Storage.Test/OperationTest.cs b/Test/Storage.Test/OperationTest.cs similarity index 100% rename from Storage/Storage.Test/OperationTest.cs rename to Test/Storage.Test/OperationTest.cs diff --git a/Storage/Storage.Test/QueryTest.cs b/Test/Storage.Test/QueryTest.cs similarity index 100% rename from Storage/Storage.Test/QueryTest.cs rename to Test/Storage.Test/QueryTest.cs diff --git a/Storage/Storage.Test/RelationTest.cs b/Test/Storage.Test/RelationTest.cs similarity index 100% rename from Storage/Storage.Test/RelationTest.cs rename to Test/Storage.Test/RelationTest.cs diff --git a/Storage/Storage.Test/RoleTest.cs b/Test/Storage.Test/RoleTest.cs similarity index 100% rename from Storage/Storage.Test/RoleTest.cs rename to Test/Storage.Test/RoleTest.cs diff --git a/Storage/Storage.Test/Storage.Test.csproj b/Test/Storage.Test/Storage.Test.csproj similarity index 89% rename from Storage/Storage.Test/Storage.Test.csproj rename to Test/Storage.Test/Storage.Test.csproj index 99a9d07..86429e9 100644 --- a/Storage/Storage.Test/Storage.Test.csproj +++ b/Test/Storage.Test/Storage.Test.csproj @@ -15,6 +15,6 @@ - + diff --git a/Storage/Storage.Test/SubClassTest.cs b/Test/Storage.Test/SubClassTest.cs similarity index 100% rename from Storage/Storage.Test/SubClassTest.cs rename to Test/Storage.Test/SubClassTest.cs diff --git a/Storage/Storage.Test/UserTest.cs b/Test/Storage.Test/UserTest.cs similarity index 100% rename from Storage/Storage.Test/UserTest.cs rename to Test/Storage.Test/UserTest.cs diff --git a/Test/Storage.Test/Utils.cs b/Test/Storage.Test/Utils.cs new file mode 100644 index 0000000..739b4aa --- /dev/null +++ b/Test/Storage.Test/Utils.cs @@ -0,0 +1,25 @@ +using System; +using LeanCloud; +using LeanCloud.Common; +using NUnit.Framework; + +namespace LeanCloud.Test { + public static class Utils { + internal static void Print(LogLevel level, string info) { + switch (level) { + case LogLevel.Debug: + TestContext.Out.WriteLine($"[DEBUG] {info}"); + break; + case LogLevel.Warn: + TestContext.Out.WriteLine($"[WARNING] {info}"); + break; + case LogLevel.Error: + TestContext.Out.WriteLine($"[ERROR] {info}"); + break; + default: + TestContext.Out.WriteLine(info); + break; + } + } + } +} diff --git a/Storage/Storage.Test/assets/hello.png b/Test/Storage.Test/assets/hello.png similarity index 100% rename from Storage/Storage.Test/assets/hello.png rename to Test/Storage.Test/assets/hello.png diff --git a/Storage/Storage.Test/assets/test.apk b/Test/Storage.Test/assets/test.apk similarity index 100% rename from Storage/Storage.Test/assets/test.apk rename to Test/Storage.Test/assets/test.apk diff --git a/csharp-sdk.sln b/csharp-sdk.sln index af711ed..9489670 100644 --- a/csharp-sdk.sln +++ b/csharp-sdk.sln @@ -1,35 +1,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Storage", "Storage", "{CD6B6669-1A56-437A-932E-BCE7F5D4CD18}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Storage.Test", "Test\Storage.Test\Storage.Test.csproj", "{BE05B492-78CD-47CA-9F48-C3E9B4813AFF}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "RTM", "RTM", "{64D8F9A1-BA44-459C-817C-788B4EBC0B9F}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "LiveQuery", "LiveQuery", "{5B895B7A-1F6E-40A5-8081-43B334D2C076}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "LeanEngine", "LeanEngine", "{CDC4E11B-592E-48C3-9105-CD49435424AA}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Storage.PCL", "Storage\Storage.PCL\Storage.PCL.csproj", "{659D19F0-9A40-42C0-886C-555E64F16848}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Storage.Unity", "Storage\Storage.Unity\Storage.Unity.csproj", "{A0D50BCB-E50E-4AAE-8E7D-24BF5AE33DAC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RTM.PCL", "RTM\RTM.PCL\RTM.PCL.csproj", "{92B2B40E-A3CD-4672-AC84-2E894E1A6CE5}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RTM.Unity", "RTM\RTM.Unity\RTM.Unity.csproj", "{1E608FCD-9039-4FF7-8EE7-BA8B00E15D1C}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RTM.Test", "RTM\RTM.Test\RTM.Test.csproj", "{A1BBD0B5-41C6-4579-B9A3-5EF778BE7F95}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LiveQuery.PCL", "LiveQuery\LiveQuery.PCL\LiveQuery.PCL.csproj", "{EA1C601E-D853-41F7-B9EB-276CBF7D1FA5}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LiveQuery.Unity", "LiveQuery\LiveQuery.Unity\LiveQuery.Unity.csproj", "{3251B4D8-D11A-4D90-8626-27FEE266B066}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LiveQuery.Test", "LiveQuery\LiveQuery.Test\LiveQuery.Test.csproj", "{F907012C-74DF-4575-AFE6-E8DAACC26D24}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Storage.Test", "Storage\Storage.Test\Storage.Test.csproj", "{BE05B492-78CD-47CA-9F48-C3E9B4813AFF}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Storage", "Storage\Storage\Storage.csproj", "{59DA32A0-4CD3-424A-8584-D08B8D1E2B98}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RTM", "RTM\RTM\RTM.csproj", "{D4A30F70-AAED-415D-B940-023B3D7241EE}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Storage", "Storage\Storage.csproj", "{59DA32A0-4CD3-424A-8584-D08B8D1E2B98}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{C827DA2F-6AB4-48D8-AB5B-6DAB925F8933}" EndProject @@ -43,38 +17,6 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {659D19F0-9A40-42C0-886C-555E64F16848}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {659D19F0-9A40-42C0-886C-555E64F16848}.Debug|Any CPU.Build.0 = Debug|Any CPU - {659D19F0-9A40-42C0-886C-555E64F16848}.Release|Any CPU.ActiveCfg = Release|Any CPU - {659D19F0-9A40-42C0-886C-555E64F16848}.Release|Any CPU.Build.0 = Release|Any CPU - {A0D50BCB-E50E-4AAE-8E7D-24BF5AE33DAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A0D50BCB-E50E-4AAE-8E7D-24BF5AE33DAC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A0D50BCB-E50E-4AAE-8E7D-24BF5AE33DAC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A0D50BCB-E50E-4AAE-8E7D-24BF5AE33DAC}.Release|Any CPU.Build.0 = Release|Any CPU - {92B2B40E-A3CD-4672-AC84-2E894E1A6CE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {92B2B40E-A3CD-4672-AC84-2E894E1A6CE5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {92B2B40E-A3CD-4672-AC84-2E894E1A6CE5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {92B2B40E-A3CD-4672-AC84-2E894E1A6CE5}.Release|Any CPU.Build.0 = Release|Any CPU - {1E608FCD-9039-4FF7-8EE7-BA8B00E15D1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1E608FCD-9039-4FF7-8EE7-BA8B00E15D1C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1E608FCD-9039-4FF7-8EE7-BA8B00E15D1C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1E608FCD-9039-4FF7-8EE7-BA8B00E15D1C}.Release|Any CPU.Build.0 = Release|Any CPU - {A1BBD0B5-41C6-4579-B9A3-5EF778BE7F95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1BBD0B5-41C6-4579-B9A3-5EF778BE7F95}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1BBD0B5-41C6-4579-B9A3-5EF778BE7F95}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1BBD0B5-41C6-4579-B9A3-5EF778BE7F95}.Release|Any CPU.Build.0 = Release|Any CPU - {EA1C601E-D853-41F7-B9EB-276CBF7D1FA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EA1C601E-D853-41F7-B9EB-276CBF7D1FA5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EA1C601E-D853-41F7-B9EB-276CBF7D1FA5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EA1C601E-D853-41F7-B9EB-276CBF7D1FA5}.Release|Any CPU.Build.0 = Release|Any CPU - {3251B4D8-D11A-4D90-8626-27FEE266B066}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3251B4D8-D11A-4D90-8626-27FEE266B066}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3251B4D8-D11A-4D90-8626-27FEE266B066}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3251B4D8-D11A-4D90-8626-27FEE266B066}.Release|Any CPU.Build.0 = Release|Any CPU - {F907012C-74DF-4575-AFE6-E8DAACC26D24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F907012C-74DF-4575-AFE6-E8DAACC26D24}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F907012C-74DF-4575-AFE6-E8DAACC26D24}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F907012C-74DF-4575-AFE6-E8DAACC26D24}.Release|Any CPU.Build.0 = Release|Any CPU {BE05B492-78CD-47CA-9F48-C3E9B4813AFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BE05B492-78CD-47CA-9F48-C3E9B4813AFF}.Debug|Any CPU.Build.0 = Debug|Any CPU {BE05B492-78CD-47CA-9F48-C3E9B4813AFF}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -83,10 +25,6 @@ Global {59DA32A0-4CD3-424A-8584-D08B8D1E2B98}.Debug|Any CPU.Build.0 = Debug|Any CPU {59DA32A0-4CD3-424A-8584-D08B8D1E2B98}.Release|Any CPU.ActiveCfg = Release|Any CPU {59DA32A0-4CD3-424A-8584-D08B8D1E2B98}.Release|Any CPU.Build.0 = Release|Any CPU - {D4A30F70-AAED-415D-B940-023B3D7241EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D4A30F70-AAED-415D-B940-023B3D7241EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D4A30F70-AAED-415D-B940-023B3D7241EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D4A30F70-AAED-415D-B940-023B3D7241EE}.Release|Any CPU.Build.0 = Release|Any CPU {4DF4E0F4-1013-477F-ADA6-BFAFD9312335}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4DF4E0F4-1013-477F-ADA6-BFAFD9312335}.Debug|Any CPU.Build.0 = Debug|Any CPU {4DF4E0F4-1013-477F-ADA6-BFAFD9312335}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -97,14 +35,6 @@ Global {758DE75D-37D7-4392-B564-9484348B505C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution - {659D19F0-9A40-42C0-886C-555E64F16848} = {CD6B6669-1A56-437A-932E-BCE7F5D4CD18} - {A0D50BCB-E50E-4AAE-8E7D-24BF5AE33DAC} = {CD6B6669-1A56-437A-932E-BCE7F5D4CD18} - {92B2B40E-A3CD-4672-AC84-2E894E1A6CE5} = {64D8F9A1-BA44-459C-817C-788B4EBC0B9F} - {1E608FCD-9039-4FF7-8EE7-BA8B00E15D1C} = {64D8F9A1-BA44-459C-817C-788B4EBC0B9F} - {A1BBD0B5-41C6-4579-B9A3-5EF778BE7F95} = {64D8F9A1-BA44-459C-817C-788B4EBC0B9F} - {EA1C601E-D853-41F7-B9EB-276CBF7D1FA5} = {5B895B7A-1F6E-40A5-8081-43B334D2C076} - {3251B4D8-D11A-4D90-8626-27FEE266B066} = {5B895B7A-1F6E-40A5-8081-43B334D2C076} - {F907012C-74DF-4575-AFE6-E8DAACC26D24} = {5B895B7A-1F6E-40A5-8081-43B334D2C076} {BE05B492-78CD-47CA-9F48-C3E9B4813AFF} = {C827DA2F-6AB4-48D8-AB5B-6DAB925F8933} {4DF4E0F4-1013-477F-ADA6-BFAFD9312335} = {C827DA2F-6AB4-48D8-AB5B-6DAB925F8933} EndGlobalSection