From e4381667c83c796b0e03f57d6536c362c841c334 Mon Sep 17 00:00:00 2001 From: oneRain Date: Wed, 19 Feb 2020 18:50:51 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E6=A1=86=E6=9E=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Common/AppRouter/AppRouterController.cs | 2 +- Storage/Storage.Test/FileTest.cs | 4 +- Storage/Storage.Test/HelloTest.cs | 25 +++ Storage/Storage/Internal/Codec/LCDecoder.cs | 77 +++++++ Storage/Storage/Internal/Codec/LCEncoder.cs | 115 ++++++++++ .../Internal/Encoding/NoObjectsEncoder.cs | 23 -- .../Storage/Internal/File/LCAWSUploader.cs | 7 + .../Storage/Internal/File/LCQiniuUploader.cs | 7 + Storage/Storage/Internal/Http/LCHttpClient.cs | 82 +++++++ Storage/Storage/Internal/Object/LCBatch.cs | 7 + .../Storage/Internal/Object/LCObjectData.cs | 73 +++++++ .../Storage/Internal/Object/LCSubClassInfo.cs | 7 + .../Internal/Operation/LCAddOperation.cs | 7 + .../Operation/LCAddRelationOperation.cs | 7 + .../Operation/LCAddUniqueOperation.cs | 7 + .../Operation/LCDecrementOperation.cs | 7 + .../Internal/Operation/LCDeleteOperation.cs | 7 + .../Operation/LCIncrementOperation.cs | 7 + .../Storage/Internal/Operation/LCOperation.cs | 7 + .../Internal/Operation/LCRemoveOperation.cs | 7 + .../Operation/LCRemoveRelationOperation.cs | 7 + .../Internal/Operation/LCSetOperation.cs | 7 + .../Internal/Query/ILCQueryCondition.cs | 8 + .../Query/LCCompositionalCondition.cs | 198 +++++++++++++++++ .../Internal/Query/LCEqualCondition.cs | 27 +++ .../Internal/Query/LCOperationCondition.cs | 31 +++ .../Internal/Query/LCRelatedCondition.cs | 30 +++ .../{Internal => Internal_}/AVCorePlugins.cs | 0 .../Cloud/Controller/AVCloudCodeController.cs | 0 .../Command/AVCommand.cs | 0 .../Command/AVCommandRunner.cs | 0 .../Command/EngineCommand.cs | 0 .../Command/RTMCommand.cs | 0 .../Encoding/AVDecoder.cs | 0 .../Encoding/AVEncoder.cs | 0 .../Encoding/AVObjectCoder.cs | 0 .../Encoding/PointerOrLocalIdEncoder.cs | 0 .../File/Controller/AVFileController.cs | 0 .../File/Controller/AWSUploader.cs | 0 .../File/Controller/QCloudUploader.cs | 0 .../File/Controller/QiniuUploader.cs | 0 .../File/Cryptography/MD5/MD5.cs | 0 .../SHA1/SHA1CryptoServiceProvider.cs | 0 .../File/State/FileState.cs | 0 .../Controller/InstallationIdController.cs | 0 .../Object/State/IObjectState.cs | 0 .../Object/State/MutableObjectState.cs | 0 .../Internal_/Object/State/ObjectData.cs | 69 ++++++ .../Object/Subclassing/ObjectSubclassInfo.cs | 0 .../ObjectSubclassingController.cs | 0 .../Operation/AVAddOperation.cs | 0 .../Operation/AVAddUniqueOperation.cs | 0 .../Operation/AVDeleteOperation.cs | 0 .../Operation/AVFieldOperations.cs | 0 .../Operation/AVIncrementOperation.cs | 0 .../Operation/AVRelationOperation.cs | 0 .../Operation/AVRemoveOperation.cs | 0 .../Operation/AVSetOperation.cs | 0 .../Operation/IAVFieldOperation.cs | 0 .../Query/Controller/AVQueryController.cs | 0 .../Query/IQueryCondition.cs | 0 .../Query/QueryCompositionalCondition.cs | 0 .../Query/QueryEqualCondition.cs | 0 .../Query/QueryOperationCondition.cs | 0 .../Query/QueryRelatedCondition.cs | 0 .../User/Controller/AVUserController.cs | 0 .../Utilities/AVObjectExtensions.cs | 0 .../Utilities/AVRelationExtensions.cs | 0 .../Utilities/FlexibleDictionaryWrapper.cs | 0 .../Utilities/FlexibleListWrapper.cs | 0 .../Utilities/IJsonConvertible.cs | 0 .../Utilities/IdentityEqualityComparer.cs | 0 .../Utilities/InternalExtensions.cs | 2 +- .../{Internal => Internal_}/Utilities/Json.cs | 0 .../Utilities/LeanCloudJsonConverter.cs | 0 .../Utilities/LockSet.cs | 0 .../Utilities/ReflectionHelpers.cs | 0 .../Utilities/SynchronizedEventHandler.cs | 0 .../Utilities/TaskQueue.cs | 0 .../Utilities/XamarinAttributes.cs | 0 Storage/Storage/LCACL.cs | 137 ++++++++++++ Storage/Storage/LCCloud.cs | 26 +++ Storage/Storage/LCException.cs | 18 ++ Storage/Storage/LCFile.cs | 7 + Storage/Storage/LCGeoPoint.cs | 78 +++++++ Storage/Storage/LCObject.cs | 45 ++++ Storage/Storage/LCQuery.cs | 7 + Storage/Storage/LCRelation.cs | 20 ++ Storage/Storage/LCRole.cs | 7 + Storage/Storage/LCUser.cs | 7 + Storage/Storage/LCUserAuthDataLoginOption.cs | 7 + Storage/Storage/LeanCloud.cs | 35 +++ Storage/Storage/Public/AVObject.cs | 200 +++++++++--------- Storage/Storage/Storage.csproj | 10 + Test/Common.Test/AppRouterTest.cs | 2 +- 95 files changed, 1346 insertions(+), 124 deletions(-) create mode 100644 Storage/Storage.Test/HelloTest.cs create mode 100644 Storage/Storage/Internal/Codec/LCDecoder.cs create mode 100644 Storage/Storage/Internal/Codec/LCEncoder.cs delete mode 100644 Storage/Storage/Internal/Encoding/NoObjectsEncoder.cs create mode 100644 Storage/Storage/Internal/File/LCAWSUploader.cs create mode 100644 Storage/Storage/Internal/File/LCQiniuUploader.cs create mode 100644 Storage/Storage/Internal/Http/LCHttpClient.cs create mode 100644 Storage/Storage/Internal/Object/LCBatch.cs create mode 100644 Storage/Storage/Internal/Object/LCObjectData.cs create mode 100644 Storage/Storage/Internal/Object/LCSubClassInfo.cs create mode 100644 Storage/Storage/Internal/Operation/LCAddOperation.cs create mode 100644 Storage/Storage/Internal/Operation/LCAddRelationOperation.cs create mode 100644 Storage/Storage/Internal/Operation/LCAddUniqueOperation.cs create mode 100644 Storage/Storage/Internal/Operation/LCDecrementOperation.cs create mode 100644 Storage/Storage/Internal/Operation/LCDeleteOperation.cs create mode 100644 Storage/Storage/Internal/Operation/LCIncrementOperation.cs create mode 100644 Storage/Storage/Internal/Operation/LCOperation.cs create mode 100644 Storage/Storage/Internal/Operation/LCRemoveOperation.cs create mode 100644 Storage/Storage/Internal/Operation/LCRemoveRelationOperation.cs create mode 100644 Storage/Storage/Internal/Operation/LCSetOperation.cs create mode 100644 Storage/Storage/Internal/Query/ILCQueryCondition.cs create mode 100644 Storage/Storage/Internal/Query/LCCompositionalCondition.cs create mode 100644 Storage/Storage/Internal/Query/LCEqualCondition.cs create mode 100644 Storage/Storage/Internal/Query/LCOperationCondition.cs create mode 100644 Storage/Storage/Internal/Query/LCRelatedCondition.cs rename Storage/Storage/{Internal => Internal_}/AVCorePlugins.cs (100%) rename Storage/Storage/{Internal => Internal_}/Cloud/Controller/AVCloudCodeController.cs (100%) rename Storage/Storage/{Internal => Internal_}/Command/AVCommand.cs (100%) rename Storage/Storage/{Internal => Internal_}/Command/AVCommandRunner.cs (100%) rename Storage/Storage/{Internal => Internal_}/Command/EngineCommand.cs (100%) rename Storage/Storage/{Internal => Internal_}/Command/RTMCommand.cs (100%) rename Storage/Storage/{Internal => Internal_}/Encoding/AVDecoder.cs (100%) rename Storage/Storage/{Internal => Internal_}/Encoding/AVEncoder.cs (100%) rename Storage/Storage/{Internal => Internal_}/Encoding/AVObjectCoder.cs (100%) rename Storage/Storage/{Internal => Internal_}/Encoding/PointerOrLocalIdEncoder.cs (100%) rename Storage/Storage/{Internal => Internal_}/File/Controller/AVFileController.cs (100%) rename Storage/Storage/{Internal => Internal_}/File/Controller/AWSUploader.cs (100%) rename Storage/Storage/{Internal => Internal_}/File/Controller/QCloudUploader.cs (100%) rename Storage/Storage/{Internal => Internal_}/File/Controller/QiniuUploader.cs (100%) rename Storage/Storage/{Internal => Internal_}/File/Cryptography/MD5/MD5.cs (100%) rename Storage/Storage/{Internal => Internal_}/File/Cryptography/SHA1/SHA1CryptoServiceProvider.cs (100%) rename Storage/Storage/{Internal => Internal_}/File/State/FileState.cs (100%) rename Storage/Storage/{Internal => Internal_}/InstallationId/Controller/InstallationIdController.cs (100%) rename Storage/Storage/{Internal => Internal_}/Object/State/IObjectState.cs (100%) rename Storage/Storage/{Internal => Internal_}/Object/State/MutableObjectState.cs (100%) create mode 100644 Storage/Storage/Internal_/Object/State/ObjectData.cs rename Storage/Storage/{Internal => Internal_}/Object/Subclassing/ObjectSubclassInfo.cs (100%) rename Storage/Storage/{Internal => Internal_}/Object/Subclassing/ObjectSubclassingController.cs (100%) rename Storage/Storage/{Internal => Internal_}/Operation/AVAddOperation.cs (100%) rename Storage/Storage/{Internal => Internal_}/Operation/AVAddUniqueOperation.cs (100%) rename Storage/Storage/{Internal => Internal_}/Operation/AVDeleteOperation.cs (100%) rename Storage/Storage/{Internal => Internal_}/Operation/AVFieldOperations.cs (100%) rename Storage/Storage/{Internal => Internal_}/Operation/AVIncrementOperation.cs (100%) rename Storage/Storage/{Internal => Internal_}/Operation/AVRelationOperation.cs (100%) rename Storage/Storage/{Internal => Internal_}/Operation/AVRemoveOperation.cs (100%) rename Storage/Storage/{Internal => Internal_}/Operation/AVSetOperation.cs (100%) rename Storage/Storage/{Internal => Internal_}/Operation/IAVFieldOperation.cs (100%) rename Storage/Storage/{Internal => Internal_}/Query/Controller/AVQueryController.cs (100%) rename Storage/Storage/{Internal => Internal_}/Query/IQueryCondition.cs (100%) rename Storage/Storage/{Internal => Internal_}/Query/QueryCompositionalCondition.cs (100%) rename Storage/Storage/{Internal => Internal_}/Query/QueryEqualCondition.cs (100%) rename Storage/Storage/{Internal => Internal_}/Query/QueryOperationCondition.cs (100%) rename Storage/Storage/{Internal => Internal_}/Query/QueryRelatedCondition.cs (100%) rename Storage/Storage/{Internal => Internal_}/User/Controller/AVUserController.cs (100%) rename Storage/Storage/{Internal => Internal_}/Utilities/AVObjectExtensions.cs (100%) rename Storage/Storage/{Internal => Internal_}/Utilities/AVRelationExtensions.cs (100%) rename Storage/Storage/{Internal => Internal_}/Utilities/FlexibleDictionaryWrapper.cs (100%) rename Storage/Storage/{Internal => Internal_}/Utilities/FlexibleListWrapper.cs (100%) rename Storage/Storage/{Internal => Internal_}/Utilities/IJsonConvertible.cs (100%) rename Storage/Storage/{Internal => Internal_}/Utilities/IdentityEqualityComparer.cs (100%) rename Storage/Storage/{Internal => Internal_}/Utilities/InternalExtensions.cs (98%) rename Storage/Storage/{Internal => Internal_}/Utilities/Json.cs (100%) rename Storage/Storage/{Internal => Internal_}/Utilities/LeanCloudJsonConverter.cs (100%) rename Storage/Storage/{Internal => Internal_}/Utilities/LockSet.cs (100%) rename Storage/Storage/{Internal => Internal_}/Utilities/ReflectionHelpers.cs (100%) rename Storage/Storage/{Internal => Internal_}/Utilities/SynchronizedEventHandler.cs (100%) rename Storage/Storage/{Internal => Internal_}/Utilities/TaskQueue.cs (100%) rename Storage/Storage/{Internal => Internal_}/Utilities/XamarinAttributes.cs (100%) create mode 100644 Storage/Storage/LCACL.cs create mode 100644 Storage/Storage/LCCloud.cs create mode 100644 Storage/Storage/LCException.cs create mode 100644 Storage/Storage/LCFile.cs create mode 100644 Storage/Storage/LCGeoPoint.cs create mode 100644 Storage/Storage/LCObject.cs create mode 100644 Storage/Storage/LCQuery.cs create mode 100644 Storage/Storage/LCRelation.cs create mode 100644 Storage/Storage/LCRole.cs create mode 100644 Storage/Storage/LCUser.cs create mode 100644 Storage/Storage/LCUserAuthDataLoginOption.cs create mode 100644 Storage/Storage/LeanCloud.cs diff --git a/Common/AppRouter/AppRouterController.cs b/Common/AppRouter/AppRouterController.cs index 3500f20..f66f19e 100644 --- a/Common/AppRouter/AppRouterController.cs +++ b/Common/AppRouter/AppRouterController.cs @@ -73,7 +73,7 @@ namespace LeanCloud.Common { response = await client.SendAsync(request); string content = await response.Content.ReadAsStringAsync(); HttpUtils.PrintResponse(response, content); - + AppRouter state = JsonConvert.DeserializeObject(content); state.Source = "router"; diff --git a/Storage/Storage.Test/FileTest.cs b/Storage/Storage.Test/FileTest.cs index 0af4303..d20e104 100644 --- a/Storage/Storage.Test/FileTest.cs +++ b/Storage/Storage.Test/FileTest.cs @@ -10,10 +10,10 @@ namespace LeanCloud.Test { [SetUp] public void SetUp() { - Utils.InitNorthChina(); + //Utils.InitNorthChina(); //Utils.InitEastChina(); //Utils.InitOldEastChina(); - //Utils.InitUS(); + Utils.InitUS(); } [Test, Order(0)] diff --git a/Storage/Storage.Test/HelloTest.cs b/Storage/Storage.Test/HelloTest.cs new file mode 100644 index 0000000..12b1f31 --- /dev/null +++ b/Storage/Storage.Test/HelloTest.cs @@ -0,0 +1,25 @@ +using NUnit.Framework; +using System.Collections.Generic; +using System.Threading.Tasks; +using LeanCloud.Storage; + +namespace LeanCloud.Test { + public class HelloTest { + [SetUp] + public void SetUp() { + LeanCloud.Initialize("ikGGdRE2YcVOemAaRbgp1xGJ-gzGzoHsz", "NUKmuRbdAhg1vrb2wexYo1jo", "https://ikggdre2.lc-cn-n1-shared.com"); + Logger.LogDelegate += Utils.Print; + } + + [Test] + public async Task Run() { + Dictionary parameters = new Dictionary { + { "name", "world" } + }; + Dictionary response = await LCCloud.Run("hello", parameters); + string ret = response["result"] as string; + TestContext.WriteLine($"ret: {ret}"); + Assert.AreEqual(ret, "hello, world"); + } + } +} diff --git a/Storage/Storage/Internal/Codec/LCDecoder.cs b/Storage/Storage/Internal/Codec/LCDecoder.cs new file mode 100644 index 0000000..661e97e --- /dev/null +++ b/Storage/Storage/Internal/Codec/LCDecoder.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using LeanCloud.Storage.Internal.Object; + +namespace LeanCloud.Storage.Internal.Codec { + internal static class LCDecoder { + internal static object Decode(object obj) { + if (obj is IDictionary dict) { + if (dict.Contains("__type")) { + string type = dict["__type"].ToString(); + if (type == "Date") { + return DecodeDate(dict); + } else if (type == "Bytes") { + return DecodeBytes(dict); + } else if (type == "Object") { + return DecodeObject(dict); + } else if (type == "Pointer") { + return DecodeObject(dict); + } else if (type == "Relation") { + return DecodeRelation(dict); + } else if (type == "GeoPoint") { + return DecodeGeoPoint(dict); + } + } + Dictionary d = new Dictionary(); + foreach (KeyValuePair kv in dict) { + string key = kv.Key; + object value = kv.Value; + d[key] = Decode(value); + } + return d; + } else if (obj is IList list) { + List l = new List(); + foreach (object o in list) { + object v = Decode(o); + l.Add(v); + } + } + return obj; + } + + static DateTime DecodeDate(IDictionary dict) { + string str = dict["iso"].ToString(); + DateTime dateTime = DateTime.Parse(str); + return dateTime.ToLocalTime(); + } + + static byte[] DecodeBytes(IDictionary dict) { + string str = dict["base64"].ToString(); + byte[] bytes = Convert.FromBase64String(str); + return bytes; + } + + static LCObject DecodeObject(IDictionary dict) { + string className = dict["className"].ToString(); + LCObject obj = LCObject.Create(className); + LCObjectData objectData = LCObjectData.Decode(dict as Dictionary); + // TODO merge + + return obj; + } + + static LCRelation DecodeRelation(IDictionary dict) { + LCRelation relation = new LCRelation(); + relation.targetClass = dict["className"].ToString(); + return relation; + } + + static LCGeoPoint DecodeGeoPoint(IDictionary data) { + double latitude = double.Parse(data["latitude"].ToString()); + double longitude = double.Parse(data["longitude"].ToString()); + LCGeoPoint geoPoint = new LCGeoPoint(latitude, longitude); + return geoPoint; + } + } +} diff --git a/Storage/Storage/Internal/Codec/LCEncoder.cs b/Storage/Storage/Internal/Codec/LCEncoder.cs new file mode 100644 index 0000000..1107138 --- /dev/null +++ b/Storage/Storage/Internal/Codec/LCEncoder.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using LeanCloud.Storage.Internal.Operation; +using LeanCloud.Storage.Internal.Query; + +namespace LeanCloud.Storage.Internal.Codec { + internal static class LCEncoder { + internal static object Encode(object obj) { + if (obj is DateTime dateTime) { + return EncodeDateTime(dateTime); + } else if (obj is byte[] bytes) { + return EncodeBytes(bytes); + } else if (obj is IList list) { + return EncodeList(list); + } else if (obj is IDictionary dict) { + return EncodeDictionary(dict); + } else if (obj is LCObject lcObj) { + return EncodeLCObject(lcObj); + } else if (obj is LCOperation op) { + return EncodeOperation(op); + } else if (obj is ILCQueryCondition cond) { + return EncodeQueryCondition(cond); + } else if (obj is LCACL acl) { + return EncodeACL(acl); + } else if (obj is LCRelation relation) { + return EncodeRelation(relation); + } else if (obj is LCGeoPoint geoPoint) { + return EncodeGeoPoint(geoPoint); + } + return obj; + } + + static object EncodeDateTime(DateTime dateTime) { + DateTime utc = dateTime.ToUniversalTime(); + string str = utc.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"); + return new Dictionary { + { "__type", "Date" }, + { "iso", str } + }; + } + + static object EncodeBytes(byte[] bytes) { + string str = Convert.ToBase64String(bytes); + return new Dictionary { + { "__type", "Bytes" }, + { "base64", str } + }; + } + + static object EncodeList(IList list) { + List l = new List(); + foreach (object obj in list) { + l.Add(Encode(obj)); + } + return l; + } + + static object EncodeDictionary(IDictionary dict) { + Dictionary d = new Dictionary(); + foreach (KeyValuePair kv in dict) { + string key = kv.Key; + object value = kv.Value; + d[key] = value; + } + return d; + } + + static object EncodeLCObject(LCObject obj) { + return new Dictionary { + { "__type", "Pointer" }, + { "className", obj.ClassName }, + { "objectId", obj.ObjectId } + }; + } + + static object EncodeOperation(LCOperation operation) { + return null; + } + + static object EncodeQueryCondition(ILCQueryCondition cond) { + return null; + } + + static object EncodeACL(LCACL acl) { + HashSet readers = acl.readers; + HashSet writers = acl.writers; + HashSet union = new HashSet(readers); + union.UnionWith(writers); + Dictionary dict = new Dictionary(); + foreach (string k in union) { + dict[k] = new Dictionary { + { "read", readers.Contains(k) }, + { "write", writers.Contains(k) } + }; + } + return dict; + } + + static object EncodeRelation(LCRelation relation) { + return new Dictionary { + { "__type", "Relation" }, + { "className", relation.targetClass } + }; + } + + static object EncodeGeoPoint(LCGeoPoint geoPoint) { + return new Dictionary { + { "__type", "GeoPoint" }, + { "latitude", geoPoint.Latitude }, + { "longitude", geoPoint.Longitude } + }; + } + } +} diff --git a/Storage/Storage/Internal/Encoding/NoObjectsEncoder.cs b/Storage/Storage/Internal/Encoding/NoObjectsEncoder.cs deleted file mode 100644 index 6a1e7e1..0000000 --- a/Storage/Storage/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/Storage/Internal/File/LCAWSUploader.cs b/Storage/Storage/Internal/File/LCAWSUploader.cs new file mode 100644 index 0000000..6f7cf23 --- /dev/null +++ b/Storage/Storage/Internal/File/LCAWSUploader.cs @@ -0,0 +1,7 @@ +using System; +namespace LeanCloud.Storage.Internal { + public class LCAWSUploader { + public LCAWSUploader() { + } + } +} diff --git a/Storage/Storage/Internal/File/LCQiniuUploader.cs b/Storage/Storage/Internal/File/LCQiniuUploader.cs new file mode 100644 index 0000000..4357253 --- /dev/null +++ b/Storage/Storage/Internal/File/LCQiniuUploader.cs @@ -0,0 +1,7 @@ +using System; +namespace LeanCloud.Storage.Internal { + public class LCQiniuUploader { + public LCQiniuUploader() { + } + } +} diff --git a/Storage/Storage/Internal/Http/LCHttpClient.cs b/Storage/Storage/Internal/Http/LCHttpClient.cs new file mode 100644 index 0000000..1df7ca3 --- /dev/null +++ b/Storage/Storage/Internal/Http/LCHttpClient.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using Newtonsoft.Json; +using LeanCloud.Common; + +namespace LeanCloud.Storage.Internal.Http { + internal class LCHttpClient { + string appId; + + string appKey; + + string server; + + string sdkVersion; + + string apiVersion; + + HttpClient client; + + internal LCHttpClient(string appId, string appKey, string server, string sdkVersion, string apiVersion) { + this.appId = appId; + this.appKey = appKey; + this.server = server; + this.sdkVersion = sdkVersion; + this.apiVersion = apiVersion; + + client = new HttpClient(); + ProductHeaderValue product = new ProductHeaderValue("LeanCloud-CSharp-SDK", LeanCloud.SDKVersion); + client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue(product)); + client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + client.DefaultRequestHeaders.Add("X-LC-Id", appId); + // TODO + client.DefaultRequestHeaders.Add("X-LC-Key", appKey); + } + + internal Task Get() { + return null; + } + + internal async Task> Post(string path, + Dictionary headers = null, + Dictionary data = null, + Dictionary queryParams = null) { + string content = (data != null) ? JsonConvert.SerializeObject(data) : null; + HttpRequestMessage request = new HttpRequestMessage { + RequestUri = new Uri($"{server}/{apiVersion}/{path}"), + Method = HttpMethod.Post, + Content = new StringContent(content) + }; + request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + HttpUtils.PrintRequest(client, request, content); + HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); + request.Dispose(); + + string resultString = await response.Content.ReadAsStringAsync(); + response.Dispose(); + HttpUtils.PrintResponse(response, resultString); + + HttpStatusCode statusCode = response.StatusCode; + if (response.IsSuccessStatusCode) { + Dictionary ret = JsonConvert.DeserializeObject>(resultString, new LeanCloudJsonConverter()); + return ret; + } + int code = (int)statusCode; + string message = resultString; + try { + // 尝试获取 LeanCloud 返回错误信息 + Dictionary error = JsonConvert.DeserializeObject>(resultString, new LeanCloudJsonConverter()); + code = (int)error["code"]; + message = error["error"].ToString(); + } catch (Exception e) { + Logger.Error(e.Message); + } finally { + throw new LCException(code, message); + } + } + } +} diff --git a/Storage/Storage/Internal/Object/LCBatch.cs b/Storage/Storage/Internal/Object/LCBatch.cs new file mode 100644 index 0000000..8e18ad9 --- /dev/null +++ b/Storage/Storage/Internal/Object/LCBatch.cs @@ -0,0 +1,7 @@ +using System; +namespace LeanCloud.Storage.Internal.Object { + public class LCBatch { + public LCBatch() { + } + } +} diff --git a/Storage/Storage/Internal/Object/LCObjectData.cs b/Storage/Storage/Internal/Object/LCObjectData.cs new file mode 100644 index 0000000..318d4bc --- /dev/null +++ b/Storage/Storage/Internal/Object/LCObjectData.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using LeanCloud.Storage.Internal.Codec; + +namespace LeanCloud.Storage.Internal.Object { + internal class LCObjectData { + internal string ClassName { + get; set; + } + + internal string ObjectId { + get; set; + } + + internal DateTime CreatedAt { + get; set; + } + + internal DateTime UpdatedAt { + get; set; + } + + internal Dictionary CustomPropertyDict; + + internal LCObjectData() { + CustomPropertyDict = new Dictionary(); + } + + internal static LCObjectData Decode(IDictionary dict) { + if (dict == null) { + return null; + } + LCObjectData objectData = new LCObjectData(); + foreach (KeyValuePair kv in dict) { + string key = kv.Key; + object value = kv.Value; + if (key == "className") { + objectData.ClassName = value.ToString(); + } else if (key == "objectId") { + objectData.ObjectId = value.ToString(); + } else if (key == "createdAt" && DateTime.TryParse(value.ToString(), out DateTime createdAt)) { + objectData.CreatedAt = createdAt; + } else if (key == "updatedAt" && DateTime.TryParse(value.ToString(), out DateTime updatedAt)) { + objectData.UpdatedAt = updatedAt; + } else { + objectData.CustomPropertyDict[key] = LCDecoder.Decode(value); + } + } + return objectData; + } + + internal static Dictionary Encode(LCObjectData objectData) { + if (objectData == null) { + return null; + } + Dictionary dict = new Dictionary { + { "className", objectData.ClassName }, + { "objectId", objectData.ObjectId }, + { "createdAt", objectData.CreatedAt }, + { "updatedAt", objectData.UpdatedAt }, + }; + if (objectData.CustomPropertyDict != null) { + foreach (KeyValuePair kv in objectData.CustomPropertyDict) { + string key = kv.Key; + object value = kv.Value; + dict[key] = LCEncoder.Encode(value); + } + } + return dict; + } + } +} diff --git a/Storage/Storage/Internal/Object/LCSubClassInfo.cs b/Storage/Storage/Internal/Object/LCSubClassInfo.cs new file mode 100644 index 0000000..da96add --- /dev/null +++ b/Storage/Storage/Internal/Object/LCSubClassInfo.cs @@ -0,0 +1,7 @@ +using System; +namespace LeanCloud.Storage.Internal.Object { + public class LCSubClassInfo { + public LCSubClassInfo() { + } + } +} diff --git a/Storage/Storage/Internal/Operation/LCAddOperation.cs b/Storage/Storage/Internal/Operation/LCAddOperation.cs new file mode 100644 index 0000000..0237844 --- /dev/null +++ b/Storage/Storage/Internal/Operation/LCAddOperation.cs @@ -0,0 +1,7 @@ +using System; +namespace LeanCloud.Storage.Internal.Operation { + public class LCAddOperation { + public LCAddOperation() { + } + } +} diff --git a/Storage/Storage/Internal/Operation/LCAddRelationOperation.cs b/Storage/Storage/Internal/Operation/LCAddRelationOperation.cs new file mode 100644 index 0000000..550e042 --- /dev/null +++ b/Storage/Storage/Internal/Operation/LCAddRelationOperation.cs @@ -0,0 +1,7 @@ +using System; +namespace LeanCloud.Storage.Internal.Operation { + public class LCAddRelationOperation { + public LCAddRelationOperation() { + } + } +} diff --git a/Storage/Storage/Internal/Operation/LCAddUniqueOperation.cs b/Storage/Storage/Internal/Operation/LCAddUniqueOperation.cs new file mode 100644 index 0000000..7787d48 --- /dev/null +++ b/Storage/Storage/Internal/Operation/LCAddUniqueOperation.cs @@ -0,0 +1,7 @@ +using System; +namespace LeanCloud.Storage.Internal.Operation { + public class LCAddUniqueOperation { + public LCAddUniqueOperation() { + } + } +} diff --git a/Storage/Storage/Internal/Operation/LCDecrementOperation.cs b/Storage/Storage/Internal/Operation/LCDecrementOperation.cs new file mode 100644 index 0000000..0e6c994 --- /dev/null +++ b/Storage/Storage/Internal/Operation/LCDecrementOperation.cs @@ -0,0 +1,7 @@ +using System; +namespace LeanCloud.Storage.Internal.Operation { + public class LCDecrementOperation { + public LCDecrementOperation() { + } + } +} diff --git a/Storage/Storage/Internal/Operation/LCDeleteOperation.cs b/Storage/Storage/Internal/Operation/LCDeleteOperation.cs new file mode 100644 index 0000000..330eae5 --- /dev/null +++ b/Storage/Storage/Internal/Operation/LCDeleteOperation.cs @@ -0,0 +1,7 @@ +using System; +namespace LeanCloud.Storage.Internal.Operation { + public class LCDeleteOperation { + public LCDeleteOperation() { + } + } +} diff --git a/Storage/Storage/Internal/Operation/LCIncrementOperation.cs b/Storage/Storage/Internal/Operation/LCIncrementOperation.cs new file mode 100644 index 0000000..644dac8 --- /dev/null +++ b/Storage/Storage/Internal/Operation/LCIncrementOperation.cs @@ -0,0 +1,7 @@ +using System; +namespace LeanCloud.Storage.Internal.Operation { + public class LCIncrementOperation { + public LCIncrementOperation() { + } + } +} diff --git a/Storage/Storage/Internal/Operation/LCOperation.cs b/Storage/Storage/Internal/Operation/LCOperation.cs new file mode 100644 index 0000000..3a63af8 --- /dev/null +++ b/Storage/Storage/Internal/Operation/LCOperation.cs @@ -0,0 +1,7 @@ +using System; +namespace LeanCloud.Storage.Internal.Operation { + public class LCOperation { + public LCOperation() { + } + } +} diff --git a/Storage/Storage/Internal/Operation/LCRemoveOperation.cs b/Storage/Storage/Internal/Operation/LCRemoveOperation.cs new file mode 100644 index 0000000..f9779bc --- /dev/null +++ b/Storage/Storage/Internal/Operation/LCRemoveOperation.cs @@ -0,0 +1,7 @@ +using System; +namespace LeanCloud.Storage.Internal.Operation { + public class LCRemoveOperation { + public LCRemoveOperation() { + } + } +} diff --git a/Storage/Storage/Internal/Operation/LCRemoveRelationOperation.cs b/Storage/Storage/Internal/Operation/LCRemoveRelationOperation.cs new file mode 100644 index 0000000..d5637de --- /dev/null +++ b/Storage/Storage/Internal/Operation/LCRemoveRelationOperation.cs @@ -0,0 +1,7 @@ +using System; +namespace LeanCloud.Storage.Internal.Operation { + public class LCRemoveRelationOperation { + public LCRemoveRelationOperation() { + } + } +} diff --git a/Storage/Storage/Internal/Operation/LCSetOperation.cs b/Storage/Storage/Internal/Operation/LCSetOperation.cs new file mode 100644 index 0000000..ddb8db5 --- /dev/null +++ b/Storage/Storage/Internal/Operation/LCSetOperation.cs @@ -0,0 +1,7 @@ +using System; +namespace LeanCloud.Storage.Internal { + public class LCSetOperation { + public LCSetOperation() { + } + } +} diff --git a/Storage/Storage/Internal/Query/ILCQueryCondition.cs b/Storage/Storage/Internal/Query/ILCQueryCondition.cs new file mode 100644 index 0000000..8dcd68d --- /dev/null +++ b/Storage/Storage/Internal/Query/ILCQueryCondition.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; + +namespace LeanCloud.Storage.Internal.Query { + internal interface ILCQueryCondition { + bool Equals(ILCQueryCondition other); + Dictionary Encode(); + } +} diff --git a/Storage/Storage/Internal/Query/LCCompositionalCondition.cs b/Storage/Storage/Internal/Query/LCCompositionalCondition.cs new file mode 100644 index 0000000..1d10962 --- /dev/null +++ b/Storage/Storage/Internal/Query/LCCompositionalCondition.cs @@ -0,0 +1,198 @@ +using System.Collections; +using System.Collections.Generic; +using LeanCloud.Storage.Internal.Codec; + +namespace LeanCloud.Storage.Internal.Query { + internal class LCCompositionalCondition : ILCQueryCondition { + internal const string And = "$and"; + internal const string Or = "$or"; + + readonly string composition; + + List conditionList; + + List orderByList; + HashSet includes; + HashSet selectedKeys; + + internal int Skip { + get; set; + } + + internal int Limit { + get; set; + } + + internal LCCompositionalCondition(string composition = And) { + this.composition = composition; + Skip = 0; + Limit = 30; + } + + // 查询条件 + internal void WhereEqualTo(string key, object value) { + Add(new LCEqualCondition(key, value)); + } + + internal void WhereNotEqualTo(string key, object value) { + AddOperation(key, "$ne", value); + } + + internal void WhereContainedIn(string key, IEnumerable values) { + AddOperation(key, "$in", values); + } + + internal void WhereNotContainedIn(string key, IEnumerable values) { + AddOperation(key, "nin", values); + } + + internal void WhereContainsAll(string key, IEnumerable values) { + AddOperation(key, "$all", values); + } + + internal void WhereExists(string key) { + AddOperation(key, "$exists", true); + } + + internal void WhereDoesNotExist(string key) { + AddOperation(key, "$exists", false); + } + + internal void WhereSizeEqualTo(string key, int size) { + AddOperation(key, "$size", size); + } + + internal void WhereGreaterThan(string key, object value) { + AddOperation(key, "$gt", value); + } + + internal void WhereGreaterThanOrEqualTo(string key, object value) { + AddOperation(key, "$gte", value); + } + + internal void WhereLessThan(string key, object value) { + AddOperation(key, "$lt$lt", value); + } + + internal void WhereLessThanOrEqualTo(string key, object value) { + AddOperation(key, "$lte", value); + } + + internal void WhereNear(string key, LCGeoPoint point) { + AddOperation(key, "$nearSphere", point); + } + + internal void WhereWithinGeoBox(string key, LCGeoPoint southwest, LCGeoPoint northeast) { + Dictionary value = new Dictionary { + { "$box", new List { southwest, northeast } } + }; + AddOperation(key, "$within", value); + } + + internal void WhereRelatedTo(LCObject parent, string key) { + Add(new LCRelatedCondition(parent, key)); + } + + internal void WhereStartsWith(string key, string prefix) { + AddOperation(key, "$regex", $"^{prefix}.*"); + } + + internal void WhereEndsWith(string key, string suffix) { + AddOperation(key, "$regex", $".*{suffix}$"); + } + + internal void WhereContains(string key, string subString) { + AddOperation(key, "$regex", $".*{subString}.*"); + } + + void AddOperation(string key, string op, object value) { + LCOperationCondition cond = new LCOperationCondition(key, op, value); + Add(cond); + } + + void Add(ILCQueryCondition cond) { + if (cond == null) { + return; + } + if (conditionList == null) { + conditionList = new List(); + } + conditionList.RemoveAll(item => item.Equals(cond)); + conditionList.Add(cond); + } + + // 筛选条件 + internal void OrderBy(string key) { + if (orderByList == null) { + orderByList = new List(); + } + orderByList.Add(key); + } + + internal void OrderByDesending(string key) { + OrderBy($"-{key}"); + } + + internal void Include(string key) { + if (includes == null) { + includes = new HashSet(); + } + includes.Add(key); + } + + internal void Select(string key) { + if (selectedKeys == null) { + selectedKeys = new HashSet(); + } + selectedKeys.Add(key); + } + + public bool Equals(ILCQueryCondition other) { + return false; + } + + public Dictionary Encode() { + if (conditionList == null || conditionList.Count == 0) { + return null; + } + if (conditionList.Count == 1) { + ILCQueryCondition cond = conditionList[0]; + return cond.Encode(); + } + return new Dictionary { + { composition, LCEncoder.Encode(conditionList) } + }; + } + + internal Dictionary BuildParams(string className) { + Dictionary dict = new Dictionary { + { "className", className }, + { "skip", Skip }, + { "limit", Limit } + }; + if (conditionList != null && conditionList.Count > 0) { + // TODO json + dict["where"] = Encode(); + } + if (orderByList != null && orderByList.Count > 0) { + dict["order"] = string.Join(",", orderByList); + } + if (includes != null && includes.Count > 0) { + dict["include"] = string.Join(",", includes); + } + if (selectedKeys != null && selectedKeys.Count > 0) { + dict["keys"] = string.Join(",", selectedKeys); + } + return dict; + } + + internal string BuildWhere() { + if (conditionList == null || conditionList.Count == 0) { + return null; + } + // TODO + + return null; + } + } +} diff --git a/Storage/Storage/Internal/Query/LCEqualCondition.cs b/Storage/Storage/Internal/Query/LCEqualCondition.cs new file mode 100644 index 0000000..57dab0d --- /dev/null +++ b/Storage/Storage/Internal/Query/LCEqualCondition.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using LeanCloud.Storage.Internal.Codec; + +namespace LeanCloud.Storage.Internal.Query { + internal class LCEqualCondition : ILCQueryCondition { + readonly string key; + readonly object value; + + internal LCEqualCondition(string key, object value) { + this.key = key; + this.value = value; + } + + public bool Equals(ILCQueryCondition other) { + if (other is LCEqualCondition cond) { + return cond.key == key; + } + return false; + } + + public Dictionary Encode() { + return new Dictionary { + { key, LCEncoder.Encode(value) } + }; + } + } +} diff --git a/Storage/Storage/Internal/Query/LCOperationCondition.cs b/Storage/Storage/Internal/Query/LCOperationCondition.cs new file mode 100644 index 0000000..79d5763 --- /dev/null +++ b/Storage/Storage/Internal/Query/LCOperationCondition.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using LeanCloud.Storage.Internal.Codec; + +namespace LeanCloud.Storage.Internal.Query { + internal class LCOperationCondition : ILCQueryCondition { + readonly string key; + readonly string op; + readonly object value; + + internal LCOperationCondition(string key, string op, object value) { + this.key = key; + this.op = op; + this.value = value; + } + + public bool Equals(ILCQueryCondition other) { + if (other is LCOperationCondition cond) { + return cond.key == key && cond.op == op; + } + return false; + } + + public Dictionary Encode() { + return new Dictionary { + { key, new Dictionary { + { op, LCEncoder.Encode(value) } + } } + }; + } + } +} diff --git a/Storage/Storage/Internal/Query/LCRelatedCondition.cs b/Storage/Storage/Internal/Query/LCRelatedCondition.cs new file mode 100644 index 0000000..6e2d2b1 --- /dev/null +++ b/Storage/Storage/Internal/Query/LCRelatedCondition.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using LeanCloud.Storage.Internal.Codec; + +namespace LeanCloud.Storage.Internal.Query { + internal class LCRelatedCondition : ILCQueryCondition { + readonly LCObject parent; + readonly string key; + + internal LCRelatedCondition(LCObject parent, string key) { + this.parent = parent; + this.key = key; + } + + public bool Equals(ILCQueryCondition other) { + if (other is LCRelatedCondition cond) { + return cond.key == key; + } + return false; + } + + public Dictionary Encode() { + return new Dictionary { + { "$relatedTo", new Dictionary { + { "object", LCEncoder.Encode(parent) }, + { "key", key } + } } + }; + } + } +} diff --git a/Storage/Storage/Internal/AVCorePlugins.cs b/Storage/Storage/Internal_/AVCorePlugins.cs similarity index 100% rename from Storage/Storage/Internal/AVCorePlugins.cs rename to Storage/Storage/Internal_/AVCorePlugins.cs diff --git a/Storage/Storage/Internal/Cloud/Controller/AVCloudCodeController.cs b/Storage/Storage/Internal_/Cloud/Controller/AVCloudCodeController.cs similarity index 100% rename from Storage/Storage/Internal/Cloud/Controller/AVCloudCodeController.cs rename to Storage/Storage/Internal_/Cloud/Controller/AVCloudCodeController.cs diff --git a/Storage/Storage/Internal/Command/AVCommand.cs b/Storage/Storage/Internal_/Command/AVCommand.cs similarity index 100% rename from Storage/Storage/Internal/Command/AVCommand.cs rename to Storage/Storage/Internal_/Command/AVCommand.cs diff --git a/Storage/Storage/Internal/Command/AVCommandRunner.cs b/Storage/Storage/Internal_/Command/AVCommandRunner.cs similarity index 100% rename from Storage/Storage/Internal/Command/AVCommandRunner.cs rename to Storage/Storage/Internal_/Command/AVCommandRunner.cs diff --git a/Storage/Storage/Internal/Command/EngineCommand.cs b/Storage/Storage/Internal_/Command/EngineCommand.cs similarity index 100% rename from Storage/Storage/Internal/Command/EngineCommand.cs rename to Storage/Storage/Internal_/Command/EngineCommand.cs diff --git a/Storage/Storage/Internal/Command/RTMCommand.cs b/Storage/Storage/Internal_/Command/RTMCommand.cs similarity index 100% rename from Storage/Storage/Internal/Command/RTMCommand.cs rename to Storage/Storage/Internal_/Command/RTMCommand.cs diff --git a/Storage/Storage/Internal/Encoding/AVDecoder.cs b/Storage/Storage/Internal_/Encoding/AVDecoder.cs similarity index 100% rename from Storage/Storage/Internal/Encoding/AVDecoder.cs rename to Storage/Storage/Internal_/Encoding/AVDecoder.cs diff --git a/Storage/Storage/Internal/Encoding/AVEncoder.cs b/Storage/Storage/Internal_/Encoding/AVEncoder.cs similarity index 100% rename from Storage/Storage/Internal/Encoding/AVEncoder.cs rename to Storage/Storage/Internal_/Encoding/AVEncoder.cs diff --git a/Storage/Storage/Internal/Encoding/AVObjectCoder.cs b/Storage/Storage/Internal_/Encoding/AVObjectCoder.cs similarity index 100% rename from Storage/Storage/Internal/Encoding/AVObjectCoder.cs rename to Storage/Storage/Internal_/Encoding/AVObjectCoder.cs diff --git a/Storage/Storage/Internal/Encoding/PointerOrLocalIdEncoder.cs b/Storage/Storage/Internal_/Encoding/PointerOrLocalIdEncoder.cs similarity index 100% rename from Storage/Storage/Internal/Encoding/PointerOrLocalIdEncoder.cs rename to Storage/Storage/Internal_/Encoding/PointerOrLocalIdEncoder.cs diff --git a/Storage/Storage/Internal/File/Controller/AVFileController.cs b/Storage/Storage/Internal_/File/Controller/AVFileController.cs similarity index 100% rename from Storage/Storage/Internal/File/Controller/AVFileController.cs rename to Storage/Storage/Internal_/File/Controller/AVFileController.cs diff --git a/Storage/Storage/Internal/File/Controller/AWSUploader.cs b/Storage/Storage/Internal_/File/Controller/AWSUploader.cs similarity index 100% rename from Storage/Storage/Internal/File/Controller/AWSUploader.cs rename to Storage/Storage/Internal_/File/Controller/AWSUploader.cs diff --git a/Storage/Storage/Internal/File/Controller/QCloudUploader.cs b/Storage/Storage/Internal_/File/Controller/QCloudUploader.cs similarity index 100% rename from Storage/Storage/Internal/File/Controller/QCloudUploader.cs rename to Storage/Storage/Internal_/File/Controller/QCloudUploader.cs diff --git a/Storage/Storage/Internal/File/Controller/QiniuUploader.cs b/Storage/Storage/Internal_/File/Controller/QiniuUploader.cs similarity index 100% rename from Storage/Storage/Internal/File/Controller/QiniuUploader.cs rename to Storage/Storage/Internal_/File/Controller/QiniuUploader.cs diff --git a/Storage/Storage/Internal/File/Cryptography/MD5/MD5.cs b/Storage/Storage/Internal_/File/Cryptography/MD5/MD5.cs similarity index 100% rename from Storage/Storage/Internal/File/Cryptography/MD5/MD5.cs rename to Storage/Storage/Internal_/File/Cryptography/MD5/MD5.cs diff --git a/Storage/Storage/Internal/File/Cryptography/SHA1/SHA1CryptoServiceProvider.cs b/Storage/Storage/Internal_/File/Cryptography/SHA1/SHA1CryptoServiceProvider.cs similarity index 100% rename from Storage/Storage/Internal/File/Cryptography/SHA1/SHA1CryptoServiceProvider.cs rename to Storage/Storage/Internal_/File/Cryptography/SHA1/SHA1CryptoServiceProvider.cs diff --git a/Storage/Storage/Internal/File/State/FileState.cs b/Storage/Storage/Internal_/File/State/FileState.cs similarity index 100% rename from Storage/Storage/Internal/File/State/FileState.cs rename to Storage/Storage/Internal_/File/State/FileState.cs diff --git a/Storage/Storage/Internal/InstallationId/Controller/InstallationIdController.cs b/Storage/Storage/Internal_/InstallationId/Controller/InstallationIdController.cs similarity index 100% rename from Storage/Storage/Internal/InstallationId/Controller/InstallationIdController.cs rename to Storage/Storage/Internal_/InstallationId/Controller/InstallationIdController.cs diff --git a/Storage/Storage/Internal/Object/State/IObjectState.cs b/Storage/Storage/Internal_/Object/State/IObjectState.cs similarity index 100% rename from Storage/Storage/Internal/Object/State/IObjectState.cs rename to Storage/Storage/Internal_/Object/State/IObjectState.cs diff --git a/Storage/Storage/Internal/Object/State/MutableObjectState.cs b/Storage/Storage/Internal_/Object/State/MutableObjectState.cs similarity index 100% rename from Storage/Storage/Internal/Object/State/MutableObjectState.cs rename to Storage/Storage/Internal_/Object/State/MutableObjectState.cs diff --git a/Storage/Storage/Internal_/Object/State/ObjectData.cs b/Storage/Storage/Internal_/Object/State/ObjectData.cs new file mode 100644 index 0000000..730adac --- /dev/null +++ b/Storage/Storage/Internal_/Object/State/ObjectData.cs @@ -0,0 +1,69 @@ +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 similarity index 100% rename from Storage/Storage/Internal/Object/Subclassing/ObjectSubclassInfo.cs rename to Storage/Storage/Internal_/Object/Subclassing/ObjectSubclassInfo.cs diff --git a/Storage/Storage/Internal/Object/Subclassing/ObjectSubclassingController.cs b/Storage/Storage/Internal_/Object/Subclassing/ObjectSubclassingController.cs similarity index 100% rename from Storage/Storage/Internal/Object/Subclassing/ObjectSubclassingController.cs rename to Storage/Storage/Internal_/Object/Subclassing/ObjectSubclassingController.cs diff --git a/Storage/Storage/Internal/Operation/AVAddOperation.cs b/Storage/Storage/Internal_/Operation/AVAddOperation.cs similarity index 100% rename from Storage/Storage/Internal/Operation/AVAddOperation.cs rename to Storage/Storage/Internal_/Operation/AVAddOperation.cs diff --git a/Storage/Storage/Internal/Operation/AVAddUniqueOperation.cs b/Storage/Storage/Internal_/Operation/AVAddUniqueOperation.cs similarity index 100% rename from Storage/Storage/Internal/Operation/AVAddUniqueOperation.cs rename to Storage/Storage/Internal_/Operation/AVAddUniqueOperation.cs diff --git a/Storage/Storage/Internal/Operation/AVDeleteOperation.cs b/Storage/Storage/Internal_/Operation/AVDeleteOperation.cs similarity index 100% rename from Storage/Storage/Internal/Operation/AVDeleteOperation.cs rename to Storage/Storage/Internal_/Operation/AVDeleteOperation.cs diff --git a/Storage/Storage/Internal/Operation/AVFieldOperations.cs b/Storage/Storage/Internal_/Operation/AVFieldOperations.cs similarity index 100% rename from Storage/Storage/Internal/Operation/AVFieldOperations.cs rename to Storage/Storage/Internal_/Operation/AVFieldOperations.cs diff --git a/Storage/Storage/Internal/Operation/AVIncrementOperation.cs b/Storage/Storage/Internal_/Operation/AVIncrementOperation.cs similarity index 100% rename from Storage/Storage/Internal/Operation/AVIncrementOperation.cs rename to Storage/Storage/Internal_/Operation/AVIncrementOperation.cs diff --git a/Storage/Storage/Internal/Operation/AVRelationOperation.cs b/Storage/Storage/Internal_/Operation/AVRelationOperation.cs similarity index 100% rename from Storage/Storage/Internal/Operation/AVRelationOperation.cs rename to Storage/Storage/Internal_/Operation/AVRelationOperation.cs diff --git a/Storage/Storage/Internal/Operation/AVRemoveOperation.cs b/Storage/Storage/Internal_/Operation/AVRemoveOperation.cs similarity index 100% rename from Storage/Storage/Internal/Operation/AVRemoveOperation.cs rename to Storage/Storage/Internal_/Operation/AVRemoveOperation.cs diff --git a/Storage/Storage/Internal/Operation/AVSetOperation.cs b/Storage/Storage/Internal_/Operation/AVSetOperation.cs similarity index 100% rename from Storage/Storage/Internal/Operation/AVSetOperation.cs rename to Storage/Storage/Internal_/Operation/AVSetOperation.cs diff --git a/Storage/Storage/Internal/Operation/IAVFieldOperation.cs b/Storage/Storage/Internal_/Operation/IAVFieldOperation.cs similarity index 100% rename from Storage/Storage/Internal/Operation/IAVFieldOperation.cs rename to Storage/Storage/Internal_/Operation/IAVFieldOperation.cs diff --git a/Storage/Storage/Internal/Query/Controller/AVQueryController.cs b/Storage/Storage/Internal_/Query/Controller/AVQueryController.cs similarity index 100% rename from Storage/Storage/Internal/Query/Controller/AVQueryController.cs rename to Storage/Storage/Internal_/Query/Controller/AVQueryController.cs diff --git a/Storage/Storage/Internal/Query/IQueryCondition.cs b/Storage/Storage/Internal_/Query/IQueryCondition.cs similarity index 100% rename from Storage/Storage/Internal/Query/IQueryCondition.cs rename to Storage/Storage/Internal_/Query/IQueryCondition.cs diff --git a/Storage/Storage/Internal/Query/QueryCompositionalCondition.cs b/Storage/Storage/Internal_/Query/QueryCompositionalCondition.cs similarity index 100% rename from Storage/Storage/Internal/Query/QueryCompositionalCondition.cs rename to Storage/Storage/Internal_/Query/QueryCompositionalCondition.cs diff --git a/Storage/Storage/Internal/Query/QueryEqualCondition.cs b/Storage/Storage/Internal_/Query/QueryEqualCondition.cs similarity index 100% rename from Storage/Storage/Internal/Query/QueryEqualCondition.cs rename to Storage/Storage/Internal_/Query/QueryEqualCondition.cs diff --git a/Storage/Storage/Internal/Query/QueryOperationCondition.cs b/Storage/Storage/Internal_/Query/QueryOperationCondition.cs similarity index 100% rename from Storage/Storage/Internal/Query/QueryOperationCondition.cs rename to Storage/Storage/Internal_/Query/QueryOperationCondition.cs diff --git a/Storage/Storage/Internal/Query/QueryRelatedCondition.cs b/Storage/Storage/Internal_/Query/QueryRelatedCondition.cs similarity index 100% rename from Storage/Storage/Internal/Query/QueryRelatedCondition.cs rename to Storage/Storage/Internal_/Query/QueryRelatedCondition.cs diff --git a/Storage/Storage/Internal/User/Controller/AVUserController.cs b/Storage/Storage/Internal_/User/Controller/AVUserController.cs similarity index 100% rename from Storage/Storage/Internal/User/Controller/AVUserController.cs rename to Storage/Storage/Internal_/User/Controller/AVUserController.cs diff --git a/Storage/Storage/Internal/Utilities/AVObjectExtensions.cs b/Storage/Storage/Internal_/Utilities/AVObjectExtensions.cs similarity index 100% rename from Storage/Storage/Internal/Utilities/AVObjectExtensions.cs rename to Storage/Storage/Internal_/Utilities/AVObjectExtensions.cs diff --git a/Storage/Storage/Internal/Utilities/AVRelationExtensions.cs b/Storage/Storage/Internal_/Utilities/AVRelationExtensions.cs similarity index 100% rename from Storage/Storage/Internal/Utilities/AVRelationExtensions.cs rename to Storage/Storage/Internal_/Utilities/AVRelationExtensions.cs diff --git a/Storage/Storage/Internal/Utilities/FlexibleDictionaryWrapper.cs b/Storage/Storage/Internal_/Utilities/FlexibleDictionaryWrapper.cs similarity index 100% rename from Storage/Storage/Internal/Utilities/FlexibleDictionaryWrapper.cs rename to Storage/Storage/Internal_/Utilities/FlexibleDictionaryWrapper.cs diff --git a/Storage/Storage/Internal/Utilities/FlexibleListWrapper.cs b/Storage/Storage/Internal_/Utilities/FlexibleListWrapper.cs similarity index 100% rename from Storage/Storage/Internal/Utilities/FlexibleListWrapper.cs rename to Storage/Storage/Internal_/Utilities/FlexibleListWrapper.cs diff --git a/Storage/Storage/Internal/Utilities/IJsonConvertible.cs b/Storage/Storage/Internal_/Utilities/IJsonConvertible.cs similarity index 100% rename from Storage/Storage/Internal/Utilities/IJsonConvertible.cs rename to Storage/Storage/Internal_/Utilities/IJsonConvertible.cs diff --git a/Storage/Storage/Internal/Utilities/IdentityEqualityComparer.cs b/Storage/Storage/Internal_/Utilities/IdentityEqualityComparer.cs similarity index 100% rename from Storage/Storage/Internal/Utilities/IdentityEqualityComparer.cs rename to Storage/Storage/Internal_/Utilities/IdentityEqualityComparer.cs diff --git a/Storage/Storage/Internal/Utilities/InternalExtensions.cs b/Storage/Storage/Internal_/Utilities/InternalExtensions.cs similarity index 98% rename from Storage/Storage/Internal/Utilities/InternalExtensions.cs rename to Storage/Storage/Internal_/Utilities/InternalExtensions.cs index e0228a5..d7a963c 100644 --- a/Storage/Storage/Internal/Utilities/InternalExtensions.cs +++ b/Storage/Storage/Internal_/Utilities/InternalExtensions.cs @@ -43,7 +43,7 @@ namespace LeanCloud.Storage.Internal { } public static bool CollectionsEqual(this IEnumerable a, IEnumerable b) { - return Object.Equals(a, b) || + return Equals(a, b) || (a != null && b != null && a.SequenceEqual(b)); } diff --git a/Storage/Storage/Internal/Utilities/Json.cs b/Storage/Storage/Internal_/Utilities/Json.cs similarity index 100% rename from Storage/Storage/Internal/Utilities/Json.cs rename to Storage/Storage/Internal_/Utilities/Json.cs diff --git a/Storage/Storage/Internal/Utilities/LeanCloudJsonConverter.cs b/Storage/Storage/Internal_/Utilities/LeanCloudJsonConverter.cs similarity index 100% rename from Storage/Storage/Internal/Utilities/LeanCloudJsonConverter.cs rename to Storage/Storage/Internal_/Utilities/LeanCloudJsonConverter.cs diff --git a/Storage/Storage/Internal/Utilities/LockSet.cs b/Storage/Storage/Internal_/Utilities/LockSet.cs similarity index 100% rename from Storage/Storage/Internal/Utilities/LockSet.cs rename to Storage/Storage/Internal_/Utilities/LockSet.cs diff --git a/Storage/Storage/Internal/Utilities/ReflectionHelpers.cs b/Storage/Storage/Internal_/Utilities/ReflectionHelpers.cs similarity index 100% rename from Storage/Storage/Internal/Utilities/ReflectionHelpers.cs rename to Storage/Storage/Internal_/Utilities/ReflectionHelpers.cs diff --git a/Storage/Storage/Internal/Utilities/SynchronizedEventHandler.cs b/Storage/Storage/Internal_/Utilities/SynchronizedEventHandler.cs similarity index 100% rename from Storage/Storage/Internal/Utilities/SynchronizedEventHandler.cs rename to Storage/Storage/Internal_/Utilities/SynchronizedEventHandler.cs diff --git a/Storage/Storage/Internal/Utilities/TaskQueue.cs b/Storage/Storage/Internal_/Utilities/TaskQueue.cs similarity index 100% rename from Storage/Storage/Internal/Utilities/TaskQueue.cs rename to Storage/Storage/Internal_/Utilities/TaskQueue.cs diff --git a/Storage/Storage/Internal/Utilities/XamarinAttributes.cs b/Storage/Storage/Internal_/Utilities/XamarinAttributes.cs similarity index 100% rename from Storage/Storage/Internal/Utilities/XamarinAttributes.cs rename to Storage/Storage/Internal_/Utilities/XamarinAttributes.cs diff --git a/Storage/Storage/LCACL.cs b/Storage/Storage/LCACL.cs new file mode 100644 index 0000000..ba424c2 --- /dev/null +++ b/Storage/Storage/LCACL.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; + +namespace LeanCloud.Storage { + /// + /// 访问控制类 + /// + public class LCACL { + const string PublicKey = "*"; + + const string RoleKeyPrefix = "role:"; + + internal HashSet readers; + internal HashSet writers; + + public bool PublicReadAccess { + get { + return GetAccess(readers, PublicKey); + } set { + SetAccess(readers, PublicKey, value); + } + } + + public bool PublicWriteAccess { + get { + return GetAccess(writers, PublicKey); + } set { + SetAccess(writers, PublicKey, value); + } + } + + public bool GetUserIdReadAccess(string userId) { + if (string.IsNullOrEmpty(userId)) { + throw new ArgumentNullException(nameof(userId)); + } + return GetAccess(readers, userId); + } + + public void SetUserIdReadAccess(string userId, bool value) { + if (string.IsNullOrEmpty(userId)) { + throw new ArgumentNullException(nameof(userId)); + } + SetAccess(readers, userId, value); + } + + public bool GetUserIdWriteAccess(string userId) { + if (string.IsNullOrEmpty(userId)) { + throw new ArgumentNullException(nameof(userId)); + } + return GetAccess(writers, userId); + } + + public void SetUserIdWriteAccess(string userId, bool value) { + if (string.IsNullOrEmpty(userId)) { + throw new ArgumentNullException(nameof(userId)); + } + SetAccess(writers, userId, value); + } + + public bool GetUserReadAccess(LCUser user) { + if (user == null) { + throw new ArgumentNullException(nameof(user)); + } + return GetUserIdReadAccess(user.ObjectId); + } + + public void SetUserReadAccess(LCUser user, bool value) { + if (user == null) { + throw new ArgumentNullException(nameof(user)); + } + SetUserIdReadAccess(user.ObjectId, value); + } + + public bool GetUserWriteAccess(LCUser user) { + if (user == null) { + throw new ArgumentNullException(nameof(user)); + } + return GetUserIdWriteAccess(user.ObjectId); + } + + public void SetUserWriteAccess(LCUser user, bool value) { + if (user == null) { + throw new ArgumentNullException(nameof(user)); + } + SetUserIdWriteAccess(user.ObjectId, value); + } + + public bool GetRoleReadAccess(LCRole role) { + if (role == null) { + throw new ArgumentNullException(nameof(role)); + } + string roleKey = $"{RoleKeyPrefix}{role.ObjectId}"; + return GetAccess(readers, roleKey); + } + + public void SetRoleReadAccess(LCRole role, bool value) { + if (role == null) { + throw new ArgumentNullException(nameof(role)); + } + string roleKey = $"{RoleKeyPrefix}{role.ObjectId}"; + SetAccess(readers, roleKey, value); + } + + public bool GetRoleWriteAccess(LCRole role) { + if (role == null) { + throw new ArgumentNullException(nameof(role)); + } + string roleKey = $"{RoleKeyPrefix}{role.ObjectId}"; + return GetAccess(writers, roleKey); + } + + public void SetRoleWriteAccess(LCRole role, bool value) { + if (role == null) { + throw new ArgumentNullException(nameof(role)); + } + string roleKey = $"{RoleKeyPrefix}{role.ObjectId}"; + SetAccess(writers, roleKey, value); + } + + public LCACL() { + readers = new HashSet(); + writers = new HashSet(); + } + + bool GetAccess(HashSet set, string key) { + return set.Contains(key); + } + + void SetAccess(HashSet set, string key, bool value) { + if (value) { + set.Add(key); + } else { + set.Remove(key); + } + } + } +} diff --git a/Storage/Storage/LCCloud.cs b/Storage/Storage/LCCloud.cs new file mode 100644 index 0000000..25252bc --- /dev/null +++ b/Storage/Storage/LCCloud.cs @@ -0,0 +1,26 @@ +using System.Threading.Tasks; +using System.Collections.Generic; + +namespace LeanCloud.Storage { + /// + /// 云引擎 + /// + public static class LCCloud { + /// + /// 调用云函数,结果为 Dictionary 类型 + /// + /// + /// + /// + public static async Task> Run(string name, Dictionary parameters = null) { + string path = $"functions/{name}"; + Dictionary response = await LeanCloud.HttpClient.Post(path, data: parameters); + return response; + } + + public static Task RPC(string name, Dictionary parameters = null) { + string path = $"call/{name}"; + return null; + } + } +} diff --git a/Storage/Storage/LCException.cs b/Storage/Storage/LCException.cs new file mode 100644 index 0000000..e58fb46 --- /dev/null +++ b/Storage/Storage/LCException.cs @@ -0,0 +1,18 @@ +using System; + +namespace LeanCloud.Storage { + public class LCException : Exception { + public int Code { + get; set; + } + + public string Message { + get; set; + } + + public LCException(int code, string message) { + Code = code; + Message = message; + } + } +} diff --git a/Storage/Storage/LCFile.cs b/Storage/Storage/LCFile.cs new file mode 100644 index 0000000..8c3d8b1 --- /dev/null +++ b/Storage/Storage/LCFile.cs @@ -0,0 +1,7 @@ +using System; +namespace LeanCloud.Storage { + public class LCFile : LCObject { + public LCFile() { + } + } +} diff --git a/Storage/Storage/LCGeoPoint.cs b/Storage/Storage/LCGeoPoint.cs new file mode 100644 index 0000000..dd5c103 --- /dev/null +++ b/Storage/Storage/LCGeoPoint.cs @@ -0,0 +1,78 @@ +using System; + +namespace LeanCloud.Storage { + public class LCGeoPoint { + /// + /// 纬度 + /// + public double Latitude { + get; + } + + /// + /// 经度 + /// + public double Longitude { + get; + } + + public LCGeoPoint(double latitude, double longtitude) { + Latitude = latitude; + Longitude = longtitude; + } + + public static LCGeoPoint Origin { + get { + return new LCGeoPoint(0, 0); + } + } + + /// + /// 据某点的距离(单位:千米) + /// + /// + /// + public double KilometersTo(LCGeoPoint point) { + if (point == null) { + throw new ArgumentNullException(nameof(point)); + } + return RadiansTo(point) * 6371.0; + } + + /// + /// 据某点的距离(单位:英里) + /// + /// + /// + public double MilesTo(LCGeoPoint point) { + if (point == null) { + throw new ArgumentNullException(nameof(point)); + } + return RadiansTo(point) * 3958.8; + } + + /// + /// 据某点的距离(单位:弧度) + /// + /// + /// + public double RadiansTo(LCGeoPoint point) { + if (point == null) { + throw new ArgumentNullException(nameof(point)); + } + double d2r = Math.PI / 180.0; + 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); + double a = sinDeltaLatDiv2 * sinDeltaLatDiv2 + + Math.Cos(lat1rad) * Math.Cos(lat2rad) * sinDeltaLongDiv2 * sinDeltaLongDiv2; + a = Math.Min(1.0, a); + return 2 * Math.Cos(Math.Sqrt(a)); + } + } +} diff --git a/Storage/Storage/LCObject.cs b/Storage/Storage/LCObject.cs new file mode 100644 index 0000000..4e5e6b6 --- /dev/null +++ b/Storage/Storage/LCObject.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using LeanCloud.Storage.Internal.Object; +using LeanCloud.Storage.Internal.Operation; + +namespace LeanCloud.Storage { + /// + /// 对象类 + /// + public class LCObject { + /// + /// 最近一次与服务端同步的数据 + /// + LCObjectData data; + + /// + /// 预算数据 + /// + Dictionary estimatedData; + + /// + /// 操作字典 + /// + Dictionary operationDict; + + public string ClassName { + get { + return data.ClassName; + } + } + + public string ObjectId { + get { + return data.ObjectId; + } + } + + public LCObject() { + } + + internal static LCObject Create(string className) { + // TODO + return null; + } + } +} diff --git a/Storage/Storage/LCQuery.cs b/Storage/Storage/LCQuery.cs new file mode 100644 index 0000000..475ce3f --- /dev/null +++ b/Storage/Storage/LCQuery.cs @@ -0,0 +1,7 @@ +using System; +namespace LeanCloud.Storage { + public class LCQuery { + public LCQuery() { + } + } +} diff --git a/Storage/Storage/LCRelation.cs b/Storage/Storage/LCRelation.cs new file mode 100644 index 0000000..58031a3 --- /dev/null +++ b/Storage/Storage/LCRelation.cs @@ -0,0 +1,20 @@ +using System; + +namespace LeanCloud.Storage { + public class LCRelation where T : LCObject { + public string Key { + get; set; + } + + public LCObject Parent { + get; set; + } + + public string targetClass { + get; set; + } + + public LCRelation() { + } + } +} diff --git a/Storage/Storage/LCRole.cs b/Storage/Storage/LCRole.cs new file mode 100644 index 0000000..9eeac7e --- /dev/null +++ b/Storage/Storage/LCRole.cs @@ -0,0 +1,7 @@ +using System; +namespace LeanCloud.Storage { + public class LCRole : LCObject { + public LCRole() { + } + } +} diff --git a/Storage/Storage/LCUser.cs b/Storage/Storage/LCUser.cs new file mode 100644 index 0000000..2c15a64 --- /dev/null +++ b/Storage/Storage/LCUser.cs @@ -0,0 +1,7 @@ +using System; +namespace LeanCloud.Storage { + public class LCUser : LCObject { + public LCUser() { + } + } +} diff --git a/Storage/Storage/LCUserAuthDataLoginOption.cs b/Storage/Storage/LCUserAuthDataLoginOption.cs new file mode 100644 index 0000000..564fed9 --- /dev/null +++ b/Storage/Storage/LCUserAuthDataLoginOption.cs @@ -0,0 +1,7 @@ +using System; +namespace LeanCloud.Storage { + public class LCUserAuthDataLoginOption { + public LCUserAuthDataLoginOption() { + } + } +} diff --git a/Storage/Storage/LeanCloud.cs b/Storage/Storage/LeanCloud.cs new file mode 100644 index 0000000..b988ad2 --- /dev/null +++ b/Storage/Storage/LeanCloud.cs @@ -0,0 +1,35 @@ +using System; +using LeanCloud.Storage.Internal.Http; + +namespace LeanCloud { + /// + /// LeanCloud 全局接口 + /// + public class LeanCloud { + // SDK 版本号,用于 User-Agent 统计 + internal const string SDKVersion = "0.1.0"; + + // 接口版本号,用于接口版本管理 + internal const string APIVersion = "1.1"; + + public static bool UseProduction { + get; set; + } + + internal static LCHttpClient HttpClient { + get; private set; + } + + public static void Initialize(string appId, string appKey, string server = null) { + if (string.IsNullOrEmpty(appId)) { + throw new ArgumentException(nameof(appId)); + } + if (string.IsNullOrEmpty(appKey)) { + throw new ArgumentException(nameof(appKey)); + } + // TODO 注册 LeanCloud 内部子类化类型 + + HttpClient = new LCHttpClient(appId, appKey, server, SDKVersion, APIVersion); + } + } +} diff --git a/Storage/Storage/Public/AVObject.cs b/Storage/Storage/Public/AVObject.cs index 364a415..48d3c67 100644 --- a/Storage/Storage/Public/AVObject.cs +++ b/Storage/Storage/Public/AVObject.cs @@ -8,7 +8,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Collections; -using System.Collections.Concurrent; namespace LeanCloud { public class AVObject : IEnumerable> { @@ -64,15 +63,15 @@ namespace LeanCloud { public ICollection Keys { get { - return estimatedData.Keys.Union(serverData.Keys).ToArray(); + return estimatedData.Keys; } } private static readonly string AutoClassName = "_Automatic"; - internal readonly ConcurrentDictionary operationDict = new ConcurrentDictionary(); - private readonly ConcurrentDictionary serverData = new ConcurrentDictionary(); - private readonly ConcurrentDictionary estimatedData = new ConcurrentDictionary(); + internal readonly Dictionary operationDict = new Dictionary(); + private Dictionary serverData = new Dictionary(); + private Dictionary estimatedData = new Dictionary(); private bool dirty; @@ -232,6 +231,9 @@ namespace LeanCloud { // 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; @@ -248,7 +250,7 @@ namespace LeanCloud { operationDict.Clear(); foreach (KeyValuePair entry in other.operationDict) { - operationDict.AddOrUpdate(entry.Key, entry.Value, (key, value) => value); + operationDict.Add(entry.Key, entry.Value); } state = other.State; @@ -293,7 +295,26 @@ namespace LeanCloud { as IDictionary; } - #region Save Object() + 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())) { @@ -371,45 +392,15 @@ namespace LeanCloud { } } - 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; - } - #endregion - #region Fetch Object(s) - - 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); - } - 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 @@ -441,9 +432,20 @@ namespace LeanCloud { }); } + #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 Object + #region Delete public virtual async Task DeleteAsync(AVQuery query = null, CancellationToken cancellationToken = default) { if (ObjectId == null) { @@ -490,48 +492,6 @@ namespace LeanCloud { PerformOperation(key, AVDeleteOperation.Instance); } - - 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(); - 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.TryRemove(key, out _); - } - - 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)); - } - } - virtual public object this[string key] { get { CheckKeyValid(key); @@ -553,11 +513,6 @@ namespace LeanCloud { } } - internal void Set(string key, object value) { - OnSettingValue(ref key, ref value); - PerformOperation(key, new AVSetOperation(value)); - } - #region Atomic Increment public void Increment(string key) { @@ -576,6 +531,8 @@ namespace LeanCloud { #endregion + #region List + public void AddToList(string key, object value) { CheckKeyValid(key); AddRangeToList(key, new[] { value }); @@ -601,6 +558,58 @@ namespace LeanCloud { 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)); @@ -613,6 +622,15 @@ namespace 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); } @@ -675,13 +693,6 @@ namespace LeanCloud { return dirty || operationDict.Count > 0; } - public void Add(string key, object value) { - if (ContainsKey(key)) { - throw new ArgumentException("Key already exists", key); - } - this[key] = value; - } - IEnumerator> IEnumerable>.GetEnumerator() { return estimatedData.GetEnumerator(); } @@ -695,7 +706,6 @@ namespace LeanCloud { return new AVQuery(className); } - #region refactor static bool HasCircleReference(object obj, HashSet parents) { if (parents.Contains(obj)) { @@ -760,8 +770,6 @@ namespace LeanCloud { return batches; } - #endregion - /// /// 保存 AVObject 时用到的辅助批次工具类 /// diff --git a/Storage/Storage/Storage.csproj b/Storage/Storage/Storage.csproj index 05ebf04..5ae4643 100644 --- a/Storage/Storage/Storage.csproj +++ b/Storage/Storage/Storage.csproj @@ -3,6 +3,7 @@ netstandard2.0 0.1.0 + LeanCloud.Storage @@ -14,4 +15,13 @@ + + + + + + + + + diff --git a/Test/Common.Test/AppRouterTest.cs b/Test/Common.Test/AppRouterTest.cs index b037029..ee9294a 100644 --- a/Test/Common.Test/AppRouterTest.cs +++ b/Test/Common.Test/AppRouterTest.cs @@ -1,7 +1,7 @@ using System; using System.Threading.Tasks; using NUnit.Framework; -using LeanCloud.Common; +using LeanCloud; namespace Common.Test { public class AppRouterTest {