diff --git a/Common/Common-Unity/Common-Unity.csproj b/Common/Common-Unity/Common-Unity.csproj
index 4bb9263..9ab3475 100644
--- a/Common/Common-Unity/Common-Unity.csproj
+++ b/Common/Common-Unity/Common-Unity.csproj
@@ -30,6 +30,9 @@
Json\LCJsonConverter.cs
+
+ Exception\LCException.cs
+
diff --git a/Common/Common/Common.csproj b/Common/Common/Common.csproj
index 8157b01..669c947 100644
--- a/Common/Common/Common.csproj
+++ b/Common/Common/Common.csproj
@@ -13,5 +13,6 @@
+
diff --git a/Storage/Storage/LCException.cs b/Common/Common/Exception/LCException.cs
similarity index 50%
rename from Storage/Storage/LCException.cs
rename to Common/Common/Exception/LCException.cs
index acf2249..1daebab 100644
--- a/Storage/Storage/LCException.cs
+++ b/Common/Common/Exception/LCException.cs
@@ -1,11 +1,20 @@
using System;
-namespace LeanCloud.Storage {
+namespace LeanCloud {
+ ///
+ /// LeanCloud 异常
+ ///
public class LCException : Exception {
+ ///
+ /// 错误码
+ ///
public int Code {
get; set;
}
+ ///
+ /// 错误信息
+ ///
public new string Message {
get; set;
}
@@ -14,5 +23,9 @@ namespace LeanCloud.Storage {
Code = code;
Message = message;
}
+
+ public override string ToString() {
+ return $"{Code} - {Message}";
+ }
}
}
diff --git a/Realtime/Realtime/Internal/Controller/LCIMConversationController.cs b/Realtime/Realtime/Internal/Controller/LCIMConversationController.cs
index 1f23ead..68798b0 100644
--- a/Realtime/Realtime/Internal/Controller/LCIMConversationController.cs
+++ b/Realtime/Realtime/Internal/Controller/LCIMConversationController.cs
@@ -52,7 +52,7 @@ namespace LeanCloud.Realtime.Internal.Controller {
attrs["name"] = name;
}
if (properties != null) {
- attrs = properties.Union(attrs)
+ attrs = properties.Union(attrs.Where(kv => !properties.ContainsKey(kv.Key)))
.ToDictionary(k => k.Key, v => v.Value);
}
conv.Attr = new JsonObjectMessage {
diff --git a/Storage/Storage-Unity/Storage-Unity.csproj b/Storage/Storage-Unity/Storage-Unity.csproj
index 7ea411a..42d8e68 100644
--- a/Storage/Storage-Unity/Storage-Unity.csproj
+++ b/Storage/Storage-Unity/Storage-Unity.csproj
@@ -15,9 +15,6 @@
LCCloud.cs
-
- LCException.cs
-
LCFile.cs
diff --git a/Storage/Storage.Test/ACLTest.cs b/Storage/Storage.Test/ACLTest.cs
index 967da0c..733cd02 100644
--- a/Storage/Storage.Test/ACLTest.cs
+++ b/Storage/Storage.Test/ACLTest.cs
@@ -1,5 +1,6 @@
using NUnit.Framework;
using System.Threading.Tasks;
+using System.Collections.ObjectModel;
using LeanCloud;
using LeanCloud.Storage;
@@ -74,5 +75,19 @@ namespace Storage.Test {
TestContext.WriteLine(account.ObjectId);
Assert.NotNull(account.ObjectId);
}
+
+ [Test]
+ public async Task Serialization() {
+ await LCUser.Login("hello", "world");
+ LCQuery query = new LCQuery("Account") {
+ IncludeACL = true
+ };
+ query.OrderByDescending("createdAt");
+ ReadOnlyCollection accounts = await query.Find();
+ foreach (LCObject account in accounts) {
+ TestContext.WriteLine($"public read access: {account.ACL.PublicReadAccess}");
+ TestContext.WriteLine($"public write access: {account.ACL.PublicWriteAccess}");
+ }
+ }
}
}
diff --git a/Storage/Storage.Test/ExceptionTest.cs b/Storage/Storage.Test/ExceptionTest.cs
index 83f70b3..bcd80c0 100644
--- a/Storage/Storage.Test/ExceptionTest.cs
+++ b/Storage/Storage.Test/ExceptionTest.cs
@@ -1,5 +1,5 @@
using NUnit.Framework;
-using LeanCloud.Storage;
+using LeanCloud;
namespace Storage.Test {
public class ExceptionTest {
diff --git a/Storage/Storage/Internal/Codec/LCDecoder.cs b/Storage/Storage/Internal/Codec/LCDecoder.cs
index f9dd00a..b4b9770 100644
--- a/Storage/Storage/Internal/Codec/LCDecoder.cs
+++ b/Storage/Storage/Internal/Codec/LCDecoder.cs
@@ -68,10 +68,25 @@ namespace LeanCloud.Storage.Internal.Codec {
}
public static LCGeoPoint DecodeGeoPoint(IDictionary data) {
- double latitude = double.Parse(data["latitude"].ToString());
- double longitude = double.Parse(data["longitude"].ToString());
+ double latitude = Convert.ToDouble(data["latitude"]);
+ double longitude = Convert.ToDouble(data["longitude"]);
LCGeoPoint geoPoint = new LCGeoPoint(latitude, longitude);
return geoPoint;
}
+
+ public static LCACL DecodeACL(Dictionary dict) {
+ LCACL acl = new LCACL();
+ foreach (KeyValuePair kv in dict) {
+ string key = kv.Key;
+ Dictionary access = kv.Value as Dictionary;
+ if (access.TryGetValue("read", out object ra)) {
+ acl.readAccess[key] = Convert.ToBoolean(ra);
+ }
+ if (access.TryGetValue("write", out object wa)) {
+ acl.writeAccess[key] = Convert.ToBoolean(wa);
+ }
+ }
+ return acl;
+ }
}
}
diff --git a/Storage/Storage/Internal/Codec/LCEncoder.cs b/Storage/Storage/Internal/Codec/LCEncoder.cs
index 383247b..a216200 100644
--- a/Storage/Storage/Internal/Codec/LCEncoder.cs
+++ b/Storage/Storage/Internal/Codec/LCEncoder.cs
@@ -83,18 +83,25 @@ namespace LeanCloud.Storage.Internal.Codec {
}
public 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) }
- };
+ HashSet keys = new HashSet();
+ if (acl.readAccess.Count > 0) {
+ keys.UnionWith(acl.readAccess.Keys);
}
- return dict;
+ if (acl.writeAccess.Count > 0) {
+ keys.UnionWith(acl.writeAccess.Keys);
+ }
+ Dictionary result = new Dictionary();
+ foreach (string key in keys) {
+ Dictionary access = new Dictionary();
+ if (acl.readAccess.TryGetValue(key, out bool ra)) {
+ access["read"] = ra;
+ }
+ if (acl.writeAccess.TryGetValue(key, out bool wa)) {
+ access["write"] = wa;
+ }
+ result[key] = access;
+ }
+ return result;
}
public static object EncodeRelation(LCRelation relation) {
diff --git a/Storage/Storage/Internal/Object/LCObjectData.cs b/Storage/Storage/Internal/Object/LCObjectData.cs
index 152c445..bc5e90d 100644
--- a/Storage/Storage/Internal/Object/LCObjectData.cs
+++ b/Storage/Storage/Internal/Object/LCObjectData.cs
@@ -40,11 +40,16 @@ namespace LeanCloud.Storage.Internal.Object {
} else if (key == "objectId") {
objectData.ObjectId = value.ToString();
} else if (key == "createdAt" && DateTime.TryParse(value.ToString(), out DateTime createdAt)) {
- objectData.CreatedAt = createdAt;
+ objectData.CreatedAt = createdAt.ToLocalTime();
} else if (key == "updatedAt" && DateTime.TryParse(value.ToString(), out DateTime updatedAt)) {
- objectData.UpdatedAt = updatedAt;
+ objectData.UpdatedAt = updatedAt.ToLocalTime();
} else {
- objectData.CustomPropertyDict[key] = LCDecoder.Decode(value);
+ if (key == "ACL" &&
+ value is Dictionary dic) {
+ objectData.CustomPropertyDict[key] = LCDecoder.DecodeACL(dic);
+ } else {
+ objectData.CustomPropertyDict[key] = LCDecoder.Decode(value);
+ }
}
}
return objectData;
@@ -61,10 +66,10 @@ namespace LeanCloud.Storage.Internal.Object {
dict["objectId"] = objectData.ObjectId;
}
if (objectData.CreatedAt != null) {
- dict["createdAt"] = objectData.CreatedAt;
+ dict["createdAt"] = objectData.CreatedAt.ToUniversalTime();
}
if (objectData.UpdatedAt != null) {
- dict["updatedAt"] = objectData.UpdatedAt;
+ dict["updatedAt"] = objectData.UpdatedAt.ToUniversalTime();
}
if (objectData.CustomPropertyDict != null) {
foreach (KeyValuePair kv in objectData.CustomPropertyDict) {
diff --git a/Storage/Storage/Internal/Query/LCCompositionalCondition.cs b/Storage/Storage/Internal/Query/LCCompositionalCondition.cs
index 426a904..7217927 100644
--- a/Storage/Storage/Internal/Query/LCCompositionalCondition.cs
+++ b/Storage/Storage/Internal/Query/LCCompositionalCondition.cs
@@ -24,6 +24,10 @@ namespace LeanCloud.Storage.Internal.Query {
get; set;
}
+ public bool IncludeACL {
+ get; set;
+ }
+
public LCCompositionalCondition(string composition = And) {
this.composition = composition;
Skip = 0;
@@ -217,6 +221,9 @@ namespace LeanCloud.Storage.Internal.Query {
if (selectedKeys != null && selectedKeys.Count > 0) {
dict["keys"] = string.Join(",", selectedKeys);
}
+ if (IncludeACL) {
+ dict["returnACL"] = "true";
+ }
return dict;
}
diff --git a/Storage/Storage/LCACL.cs b/Storage/Storage/LCACL.cs
index b7b6664..024c48f 100644
--- a/Storage/Storage/LCACL.cs
+++ b/Storage/Storage/LCACL.cs
@@ -10,22 +10,32 @@ namespace LeanCloud.Storage {
const string RoleKeyPrefix = "role:";
- internal HashSet readers;
- internal HashSet writers;
+ internal Dictionary readAccess = new Dictionary();
+ internal Dictionary writeAccess = new Dictionary();
+
+ public static LCACL CreateWithOwner(LCUser owner) {
+ if (owner == null) {
+ throw new ArgumentNullException(nameof(owner));
+ }
+ LCACL acl = new LCACL();
+ acl.SetUserReadAccess(owner, true);
+ acl.SetUserWriteAccess(owner, true);
+ return acl;
+ }
public bool PublicReadAccess {
get {
- return GetAccess(readers, PublicKey);
+ return GetAccess(readAccess, PublicKey);
} set {
- SetAccess(readers, PublicKey, value);
+ SetAccess(readAccess, PublicKey, value);
}
}
public bool PublicWriteAccess {
get {
- return GetAccess(writers, PublicKey);
+ return GetAccess(writeAccess, PublicKey);
} set {
- SetAccess(writers, PublicKey, value);
+ SetAccess(writeAccess, PublicKey, value);
}
}
@@ -33,28 +43,28 @@ namespace LeanCloud.Storage {
if (string.IsNullOrEmpty(userId)) {
throw new ArgumentNullException(nameof(userId));
}
- return GetAccess(readers, userId);
+ return GetAccess(readAccess, userId);
}
public void SetUserIdReadAccess(string userId, bool value) {
if (string.IsNullOrEmpty(userId)) {
throw new ArgumentNullException(nameof(userId));
}
- SetAccess(readers, userId, value);
+ SetAccess(readAccess, userId, value);
}
public bool GetUserIdWriteAccess(string userId) {
if (string.IsNullOrEmpty(userId)) {
throw new ArgumentNullException(nameof(userId));
}
- return GetAccess(writers, userId);
+ return GetAccess(writeAccess, userId);
}
public void SetUserIdWriteAccess(string userId, bool value) {
if (string.IsNullOrEmpty(userId)) {
throw new ArgumentNullException(nameof(userId));
}
- SetAccess(writers, userId, value);
+ SetAccess(writeAccess, userId, value);
}
public bool GetUserReadAccess(LCUser user) {
@@ -90,7 +100,7 @@ namespace LeanCloud.Storage {
throw new ArgumentNullException(nameof(role));
}
string roleKey = $"{RoleKeyPrefix}{role.ObjectId}";
- return GetAccess(readers, roleKey);
+ return GetAccess(readAccess, roleKey);
}
public void SetRoleReadAccess(LCRole role, bool value) {
@@ -98,7 +108,7 @@ namespace LeanCloud.Storage {
throw new ArgumentNullException(nameof(role));
}
string roleKey = $"{RoleKeyPrefix}{role.ObjectId}";
- SetAccess(readers, roleKey, value);
+ SetAccess(readAccess, roleKey, value);
}
public bool GetRoleWriteAccess(LCRole role) {
@@ -106,7 +116,7 @@ namespace LeanCloud.Storage {
throw new ArgumentNullException(nameof(role));
}
string roleKey = $"{RoleKeyPrefix}{role.ObjectId}";
- return GetAccess(writers, roleKey);
+ return GetAccess(writeAccess, roleKey);
}
public void SetRoleWriteAccess(LCRole role, bool value) {
@@ -114,34 +124,16 @@ namespace LeanCloud.Storage {
throw new ArgumentNullException(nameof(role));
}
string roleKey = $"{RoleKeyPrefix}{role.ObjectId}";
- SetAccess(writers, roleKey, value);
+ SetAccess(writeAccess, roleKey, value);
}
- public LCACL() {
- readers = new HashSet();
- writers = new HashSet();
+ bool GetAccess(Dictionary access, string key) {
+ return access.ContainsKey(key) ?
+ access[key] : false;
}
- public static LCACL CreateWithOwner(LCUser owner) {
- if (owner == null) {
- throw new ArgumentNullException(nameof(owner));
- }
- LCACL acl = new LCACL();
- acl.SetUserReadAccess(owner, true);
- acl.SetUserWriteAccess(owner, true);
- return acl;
- }
-
- 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);
- }
+ void SetAccess(Dictionary access, string key, bool value) {
+ access[key] = value;
}
}
}
diff --git a/Storage/Storage/LCCloud.cs b/Storage/Storage/LCCloud.cs
index 830c8e0..01066c7 100644
--- a/Storage/Storage/LCCloud.cs
+++ b/Storage/Storage/LCCloud.cs
@@ -8,20 +8,31 @@ namespace LeanCloud.Storage {
///
public static class LCCloud {
///
- /// 调用云函数,结果为 Dictionary 类型
+ /// 调用云函数
///
///
///
- ///
- public static async Task> Run(string name, Dictionary parameters = null) {
+ /// 返回类型为 Dictionary
+ public static async Task> Run(string name,
+ Dictionary parameters = null) {
string path = $"functions/{name}";
- Dictionary response = await LCApplication.HttpClient.Post>(path, data: parameters);
+ object encodeParams = LCEncoder.Encode(parameters);
+ Dictionary response = await LCApplication.HttpClient.Post>(path,
+ data: encodeParams);
return response;
}
- public static async Task