* ObjectTest.cs: chore: 完善 Object 模块和测试
* LCQuery.cs: * LCObject.cs: * LCDecoder.cs: * LCEncoder.cs: * LCHttpClient.cs: * ILCOperation.cs: * LCAddOperation.cs: * LCSetOperation.cs: * LCDeleteOperation.cs: * LCRemoveOperation.cs: * LCAddUniqueOperation.cs: * LCDecrementOperation.cs: * LCCompositionalCondition.cs: * LCAddRelationOperation.cs: * LCRemoveRelationOperation.cs:
parent
c775f612bc
commit
05047115d1
|
|
@ -1,16 +1,15 @@
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using LeanCloud.Common;
|
using LeanCloud.Storage;
|
||||||
|
|
||||||
namespace LeanCloud.Test {
|
namespace LeanCloud.Test {
|
||||||
public class ObjectTests {
|
public class ObjectTest {
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void SetUp() {
|
public void SetUp() {
|
||||||
Logger.LogDelegate += Utils.Print;
|
Logger.LogDelegate += Utils.Print;
|
||||||
Utils.InitNorthChina();
|
LeanCloud.Initialize("ikGGdRE2YcVOemAaRbgp1xGJ-gzGzoHsz", "NUKmuRbdAhg1vrb2wexYo1jo", "https://ikggdre2.lc-cn-n1-shared.com");
|
||||||
}
|
}
|
||||||
|
|
||||||
[TearDown]
|
[TearDown]
|
||||||
|
|
@ -19,296 +18,130 @@ namespace LeanCloud.Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task Save() {
|
public async Task CreateObject() {
|
||||||
AVObject obj = AVObject.Create("Foo");
|
LCObject @object = new LCObject("Hello");
|
||||||
obj["content"] = "hello, world";
|
@object["intValue"] = 123;
|
||||||
obj["list"] = new List<int> { 1, 1, 2, 3, 5, 8 };
|
@object["boolValue"] = true;
|
||||||
obj["dict"] = new Dictionary<string, int> {
|
@object["stringValue"] = "hello, world";
|
||||||
{ "hello", 1 },
|
@object["time"] = DateTime.Now;
|
||||||
{ "world", 2 }
|
@object["intList"] = new List<int> { 1, 1, 2, 3, 5, 8 };
|
||||||
|
@object["stringMap"] = new Dictionary<string, object> {
|
||||||
|
{ "k1", 111 },
|
||||||
|
{ "k2", true },
|
||||||
|
{ "k3", "haha" }
|
||||||
};
|
};
|
||||||
await obj.SaveAsync(true);
|
LCObject nestedObj = new LCObject("World");
|
||||||
Assert.NotNull(obj.ObjectId);
|
nestedObj["content"] = "7788";
|
||||||
Assert.NotNull(obj.CreatedAt);
|
@object["objectValue"] = nestedObj;
|
||||||
Assert.NotNull(obj.UpdatedAt);
|
@object["pointerList"] = new List<object> { new LCObject("World"), nestedObj };
|
||||||
|
await @object.Save();
|
||||||
|
|
||||||
|
TestContext.WriteLine(@object.ClassName);
|
||||||
|
TestContext.WriteLine(@object.ObjectId);
|
||||||
|
TestContext.WriteLine(@object.CreatedAt);
|
||||||
|
TestContext.WriteLine(@object.UpdatedAt);
|
||||||
|
TestContext.WriteLine(@object["intValue"]);
|
||||||
|
TestContext.WriteLine(@object["boolValue"]);
|
||||||
|
TestContext.WriteLine(@object["stringValue"]);
|
||||||
|
TestContext.WriteLine(@object["objectValue"]);
|
||||||
|
TestContext.WriteLine(@object["time"]);
|
||||||
|
|
||||||
|
Assert.AreEqual(nestedObj, @object["objectValue"]);
|
||||||
|
TestContext.WriteLine(nestedObj.ClassName);
|
||||||
|
TestContext.WriteLine(nestedObj.ObjectId);
|
||||||
|
|
||||||
|
Assert.NotNull(@object.ObjectId);
|
||||||
|
Assert.NotNull(@object.ClassName);
|
||||||
|
Assert.NotNull(@object.CreatedAt);
|
||||||
|
Assert.NotNull(@object.UpdatedAt);
|
||||||
|
Assert.AreEqual(@object["intValue"], 123);
|
||||||
|
Assert.AreEqual(@object["boolValue"], true);
|
||||||
|
Assert.AreEqual(@object["stringValue"], "hello, world");
|
||||||
|
|
||||||
|
Assert.NotNull(nestedObj);
|
||||||
|
Assert.NotNull(nestedObj.ClassName);
|
||||||
|
Assert.NotNull(nestedObj.ObjectId);
|
||||||
|
Assert.NotNull(nestedObj.CreatedAt);
|
||||||
|
Assert.NotNull(nestedObj.UpdatedAt);
|
||||||
|
|
||||||
|
List<object> pointerList = @object["pointerList"] as List<object>;
|
||||||
|
foreach (object pointerObj in pointerList) {
|
||||||
|
LCObject pointer = pointerObj as LCObject;
|
||||||
|
Assert.NotNull(pointer.ObjectId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task SaveWithOptions() {
|
public async Task SaveAll() {
|
||||||
AVObject account = AVObject.CreateWithoutData("Account", "5d65fa5330863b008065e476");
|
List<LCObject> list = new List<LCObject>();
|
||||||
account["balance"] = 100;
|
|
||||||
await account.SaveAsync();
|
|
||||||
AVQuery<AVObject> query = new AVQuery<AVObject>("Account");
|
|
||||||
query.WhereGreaterThan("balance", 80);
|
|
||||||
account["balance"] = 50;
|
|
||||||
await account.SaveAsync(true, query);
|
|
||||||
TestContext.Out.WriteLine($"balance: {account["balance"]}");
|
|
||||||
}
|
|
||||||
|
|
||||||
//[Test]
|
|
||||||
//public async Task SaveWithPointer() {
|
|
||||||
// AVObject comment = new AVObject("Comment") {
|
|
||||||
// { "content", "Hello, Comment" }
|
|
||||||
// };
|
|
||||||
|
|
||||||
// AVObject post = new AVObject("Post") {
|
|
||||||
// { "name", "New Post" },
|
|
||||||
// { "category", new AVObject("Category") {
|
|
||||||
// { "name", "new post category" }
|
|
||||||
// } }
|
|
||||||
// };
|
|
||||||
// comment["post"] = post;
|
|
||||||
|
|
||||||
// AVObject testPost = new AVObject("Post") {
|
|
||||||
// { "name", "Test Post" },
|
|
||||||
// { "category", new AVObject("Category") {
|
|
||||||
// { "name", "test post category" }
|
|
||||||
// } }
|
|
||||||
// };
|
|
||||||
// comment["test_post"] = testPost;
|
|
||||||
|
|
||||||
// await comment.Save();
|
|
||||||
// TestContext.Out.WriteLine(post);
|
|
||||||
// TestContext.Out.WriteLine(testPost);
|
|
||||||
// TestContext.Out.WriteLine(comment);
|
|
||||||
//}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public async Task SaveWithPointer() {
|
|
||||||
AVObject parent = new AVObject("Parent");
|
|
||||||
AVObject c1 = new AVObject("C1");
|
|
||||||
AVObject c2 = new AVObject("C2");
|
|
||||||
parent["c1"] = c1;
|
|
||||||
parent["c2"] = c2;
|
|
||||||
await parent.SaveAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public async Task SaveWithPointerArray() {
|
|
||||||
AVObject parent = new AVObject("Parent");
|
|
||||||
AVObject c1 = new AVObject("C1");
|
|
||||||
AVObject c2 = new AVObject("C2");
|
|
||||||
parent["iList"] = new List<int> { 1, 1, 2, 3 };
|
|
||||||
parent["cList"] = new List<AVObject> { c1, c2 };
|
|
||||||
parent["cDict"] = new Dictionary<string, AVObject> {
|
|
||||||
{ "c1", c1 },
|
|
||||||
{ "c2", c2 }
|
|
||||||
};
|
|
||||||
await parent.SaveAsync();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public async Task SaveBatch() {
|
|
||||||
List<AVObject> objList = new List<AVObject>();
|
|
||||||
for (int i = 0; i < 5; i++) {
|
for (int i = 0; i < 5; i++) {
|
||||||
AVObject obj = AVObject.Create("Foo");
|
LCObject world = new LCObject("World");
|
||||||
obj["content"] = "batch object";
|
world["content"] = $"word_{i}";
|
||||||
objList.Add(obj);
|
list.Add(world);
|
||||||
}
|
}
|
||||||
try {
|
await LCObject.SaveAll(list);
|
||||||
await objList.SaveAllAsync();
|
foreach (LCObject obj in list) {
|
||||||
objList.ForEach(obj => {
|
|
||||||
Assert.NotNull(obj.ObjectId);
|
Assert.NotNull(obj.ObjectId);
|
||||||
});
|
|
||||||
} catch (Exception e) {
|
|
||||||
TestContext.Out.WriteLine(e.Message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
|
||||||
public async Task Fetch() {
|
|
||||||
AVObject obj = AVObject.CreateWithoutData("Todo", "5d5f6039d5de2b006cf29c8f");
|
|
||||||
await obj.FetchAsync();
|
|
||||||
Assert.NotNull(obj["title"]);
|
|
||||||
Assert.NotNull(obj["content"]);
|
|
||||||
TestContext.Out.WriteLine($"{obj["title"]}, {obj["content"]}");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public async Task FetchWithKeys() {
|
|
||||||
AVObject obj = AVObject.CreateWithoutData("Post", "5d3abfa530863b0068e1b326");
|
|
||||||
await obj.FetchAsync(new List<string> { "pubUser" });
|
|
||||||
TestContext.Out.WriteLine($"{obj["pubUser"]}");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public async Task FetchWithIncludes() {
|
|
||||||
AVObject obj = AVObject.CreateWithoutData("Post", "5d3abfa530863b0068e1b326");
|
|
||||||
await obj.FetchAsync(includes: new List<string> { "tag" });
|
|
||||||
AVObject tag = obj["tag"] as AVObject;
|
|
||||||
TestContext.Out.WriteLine($"{tag["name"]}");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public async Task FetchAll() {
|
|
||||||
List<AVObject> objList = new List<AVObject> {
|
|
||||||
AVObject.CreateWithoutData("Tag", "5d64e5ebc05a8000730340ba"),
|
|
||||||
AVObject.CreateWithoutData("Tag", "5d64e5eb12215f0073db271c"),
|
|
||||||
AVObject.CreateWithoutData("Tag", "5d64e57f43e78c0068a14315")
|
|
||||||
};
|
|
||||||
await objList.FetchAllAsync();
|
|
||||||
objList.ForEach(obj => {
|
|
||||||
Assert.NotNull(obj.ObjectId);
|
|
||||||
TestContext.Out.WriteLine($"{obj.ObjectId}, {obj["name"]}");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task Delete() {
|
public async Task Delete() {
|
||||||
AVObject obj = AVObject.Create("Foo");
|
LCObject world = new LCObject("World");
|
||||||
obj["content"] = "hello, world";
|
await world.Save();
|
||||||
await obj.SaveAsync();
|
await world.Delete();
|
||||||
Assert.NotNull(obj);
|
|
||||||
await obj.DeleteAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public async Task DeleteWithCondition() {
|
|
||||||
AVObject account = new AVObject("Account") {
|
|
||||||
{ "balance", 100 },
|
|
||||||
};
|
|
||||||
account.ACL = new AVACL {
|
|
||||||
PublicWriteAccess = true,
|
|
||||||
PublicReadAccess = true
|
|
||||||
};
|
|
||||||
await account.SaveAsync();
|
|
||||||
AVQuery<AVObject> condition = new AVQuery<AVObject>();
|
|
||||||
condition.WhereGreaterThan("balance", 10);
|
|
||||||
await account.DeleteAsync(condition);
|
|
||||||
|
|
||||||
account = new AVObject("Account") {
|
|
||||||
{ "name", "acl account" },
|
|
||||||
{ "balance", 8 },
|
|
||||||
};
|
|
||||||
account.ACL = new AVACL {
|
|
||||||
PublicWriteAccess = true,
|
|
||||||
PublicReadAccess = true
|
|
||||||
};
|
|
||||||
await account.SaveAsync();
|
|
||||||
condition = new AVQuery<AVObject>();
|
|
||||||
condition.WhereGreaterThan("balance", 10);
|
|
||||||
try {
|
|
||||||
await account.DeleteAsync(condition);
|
|
||||||
} catch (AVException e) {
|
|
||||||
Assert.AreEqual(e.Code, AVException.ErrorCode.NoEffectOnUpdatingOrDeleting);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task DeleteAll() {
|
public async Task DeleteAll() {
|
||||||
List<AVObject> objList = new List<AVObject>();
|
List<LCObject> list = new List<LCObject> {
|
||||||
for (int i = 0; i < 5; i++) {
|
new LCObject("World"),
|
||||||
AVObject obj = AVObject.Create("Foo");
|
new LCObject("World"),
|
||||||
obj.ACL = new AVACL {
|
new LCObject("World"),
|
||||||
PublicReadAccess = true,
|
new LCObject("World")
|
||||||
PublicWriteAccess = i % 2 == 0
|
|
||||||
};
|
};
|
||||||
obj["content"] = "batch object";
|
await LCObject.SaveAll(list);
|
||||||
objList.Add(obj);
|
await LCObject.DeleteAll(list);
|
||||||
}
|
}
|
||||||
await objList.SaveAllAsync();
|
|
||||||
|
[Test]
|
||||||
|
public async Task Fetch() {
|
||||||
|
LCObject hello = LCObject.CreateWithoutData("Hello", "5e14392743c257006fb769d5");
|
||||||
|
await hello.Fetch(includes: new List<string> { "objectValue" });
|
||||||
|
LCObject world = hello["objectValue"] as LCObject;
|
||||||
|
TestContext.WriteLine(world["content"]);
|
||||||
|
Assert.AreEqual(world["content"], "7788");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task SaveWithOption() {
|
||||||
|
LCObject account = new LCObject("Account");
|
||||||
|
account["balance"] = 10;
|
||||||
|
await account.Save();
|
||||||
|
|
||||||
|
account["balance"] = 1000;
|
||||||
|
LCQuery<LCObject> q = new LCQuery<LCObject>("Account");
|
||||||
|
q.WhereGreaterThan("balance", 100);
|
||||||
try {
|
try {
|
||||||
await AVObject.DeleteAllAsync(objList);
|
await account.Save(fetchWhenSave: true, query: q);
|
||||||
} catch (AggregateException e) {
|
} catch(LCException e) {
|
||||||
foreach (AVException ie in e.InnerExceptions) {
|
TestContext.WriteLine($"{e.Code} : {e.Message}");
|
||||||
TestContext.Out.WriteLine($"{ie.Code} : {ie.Message}");
|
Assert.AreEqual(e.Code, 305);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task TestMassiveRequest() {
|
public async Task Unset() {
|
||||||
ThreadPool.SetMaxThreads(1, 1);
|
LCObject hello = new LCObject("Hello");
|
||||||
await Task.Run(() => {
|
hello["content"] = "hello, world";
|
||||||
for (int i = 0; i < 10; i++) {
|
await hello.Save();
|
||||||
for (int j = 0; j < 50; j++) {
|
TestContext.WriteLine(hello["content"]);
|
||||||
AVObject obj = AVObject.Create("Foo");
|
Assert.AreEqual(hello["content"], "hello, world");
|
||||||
obj.SaveAsync().ContinueWith(_ => {
|
|
||||||
TestContext.Out.WriteLine($"{obj.ObjectId} saved at {Thread.CurrentThread.ManagedThreadId}");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Thread.Sleep(1000);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
hello.Unset("content");
|
||||||
public void SimpleCircleReference() {
|
await hello.Save();
|
||||||
AVObject a = new AVObject("A");
|
TestContext.WriteLine(hello["content"]);
|
||||||
AVObject b = new AVObject("B");
|
Assert.IsNull(hello["content"]);
|
||||||
a["b"] = b;
|
|
||||||
b["a"] = a;
|
|
||||||
|
|
||||||
Assert.ThrowsAsync<AVException>(async () => await a.SaveAsync());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void IndirectCircleReference() {
|
|
||||||
AVObject a = new AVObject("A");
|
|
||||||
AVObject b = new AVObject("B");
|
|
||||||
AVObject c = new AVObject("C");
|
|
||||||
a["b"] = b;
|
|
||||||
b["c"] = c;
|
|
||||||
c["a"] = a;
|
|
||||||
|
|
||||||
Assert.ThrowsAsync<AVException>(async () => await a.SaveAsync());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void SimpleCollectionPointerCircleReference() {
|
|
||||||
AVObject a = new AVObject("A");
|
|
||||||
AVObject b = new AVObject("B");
|
|
||||||
a["children"] = new List<object> { 1, b };
|
|
||||||
b["children"] = new Dictionary<string, object> {
|
|
||||||
{ "c", a }
|
|
||||||
};
|
|
||||||
|
|
||||||
Assert.ThrowsAsync<AVException>(async () => await a.SaveAsync());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void IndirectCollectionPointerCircleReference() {
|
|
||||||
AVObject a = new AVObject("A");
|
|
||||||
AVObject b = new AVObject("B");
|
|
||||||
AVObject c = new AVObject("C");
|
|
||||||
|
|
||||||
a["children"] = new List<object> { 1, b };
|
|
||||||
b["children"] = new List<object> { 2, c };
|
|
||||||
c["children"] = new Dictionary<string, object> {
|
|
||||||
{ "c", a }
|
|
||||||
};
|
|
||||||
|
|
||||||
Assert.ThrowsAsync<AVException>(async () => await a.SaveAsync());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public async Task SimpleSavePointerCollection() {
|
|
||||||
AVObject p = new AVObject("P");
|
|
||||||
AVObject c1 = new AVObject("C1");
|
|
||||||
AVObject c2 = new AVObject("C2");
|
|
||||||
p["cList"] = new List<AVObject> { c1, c2 };
|
|
||||||
p["cDict"] = new Dictionary<string, object> {
|
|
||||||
{ "c1", c1 },
|
|
||||||
{ "c2", c2 }
|
|
||||||
};
|
|
||||||
await p.SaveAsync();
|
|
||||||
Assert.NotNull(p.ObjectId);
|
|
||||||
Assert.NotNull(p.CreatedAt);
|
|
||||||
Assert.NotNull(c1.ObjectId);
|
|
||||||
Assert.NotNull(c2.ObjectId);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public async Task SaveWithExistedObject() {
|
|
||||||
AVObject p = new AVObject("P");
|
|
||||||
AVObject c1 = AVObject.CreateWithoutData("C1", "5dea05578a84ab00680b7ae5");
|
|
||||||
AVObject c2 = new AVObject("C2");
|
|
||||||
p["c"] = c1;
|
|
||||||
c1["c"] = c2;
|
|
||||||
await p.SaveAsync();
|
|
||||||
Assert.NotNull(p.ObjectId);
|
|
||||||
Assert.NotNull(p.CreatedAt);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,8 +56,7 @@ namespace LeanCloud.Storage.Internal.Codec {
|
||||||
string className = dict["className"].ToString();
|
string className = dict["className"].ToString();
|
||||||
LCObject obj = LCObject.Create(className);
|
LCObject obj = LCObject.Create(className);
|
||||||
LCObjectData objectData = LCObjectData.Decode(dict as Dictionary<string, object>);
|
LCObjectData objectData = LCObjectData.Decode(dict as Dictionary<string, object>);
|
||||||
// TODO merge
|
obj.Merge(objectData);
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,10 +58,10 @@ namespace LeanCloud.Storage.Internal.Codec {
|
||||||
|
|
||||||
static object EncodeDictionary(IDictionary dict) {
|
static object EncodeDictionary(IDictionary dict) {
|
||||||
Dictionary<string, object> d = new Dictionary<string, object>();
|
Dictionary<string, object> d = new Dictionary<string, object>();
|
||||||
foreach (KeyValuePair<string, object> kv in dict) {
|
foreach (DictionaryEntry entry in dict) {
|
||||||
string key = kv.Key;
|
string key = entry.Key.ToString();
|
||||||
object value = kv.Value;
|
object value = entry.Value;
|
||||||
d[key] = value;
|
d[key] = Encode(value);
|
||||||
}
|
}
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
@ -75,11 +75,11 @@ namespace LeanCloud.Storage.Internal.Codec {
|
||||||
}
|
}
|
||||||
|
|
||||||
static object EncodeOperation(ILCOperation operation) {
|
static object EncodeOperation(ILCOperation operation) {
|
||||||
return null;
|
return operation.Encode();
|
||||||
}
|
}
|
||||||
|
|
||||||
static object EncodeQueryCondition(ILCQueryCondition cond) {
|
static object EncodeQueryCondition(ILCQueryCondition cond) {
|
||||||
return null;
|
return cond.Encode();
|
||||||
}
|
}
|
||||||
|
|
||||||
static object EncodeACL(LCACL acl) {
|
static object EncodeACL(LCACL acl) {
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ namespace LeanCloud.Storage.Internal.Http {
|
||||||
client.DefaultRequestHeaders.Add("X-LC-Key", appKey);
|
client.DefaultRequestHeaders.Add("X-LC-Key", appKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<Dictionary<string, object>> Get(string path,
|
internal async Task<T> Get<T>(string path,
|
||||||
Dictionary<string, object> headers = null,
|
Dictionary<string, object> headers = null,
|
||||||
Dictionary<string, object> queryParams = null) {
|
Dictionary<string, object> queryParams = null) {
|
||||||
|
|
||||||
|
|
@ -53,6 +53,11 @@ namespace LeanCloud.Storage.Internal.Http {
|
||||||
RequestUri = new Uri(url),
|
RequestUri = new Uri(url),
|
||||||
Method = HttpMethod.Get
|
Method = HttpMethod.Get
|
||||||
};
|
};
|
||||||
|
if (headers != null) {
|
||||||
|
foreach (KeyValuePair<string, object> kv in headers) {
|
||||||
|
request.Headers.Add(kv.Key, kv.Value.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
HttpUtils.PrintRequest(client, request);
|
HttpUtils.PrintRequest(client, request);
|
||||||
HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
|
HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
|
||||||
request.Dispose();
|
request.Dispose();
|
||||||
|
|
@ -63,7 +68,7 @@ namespace LeanCloud.Storage.Internal.Http {
|
||||||
|
|
||||||
HttpStatusCode statusCode = response.StatusCode;
|
HttpStatusCode statusCode = response.StatusCode;
|
||||||
if (response.IsSuccessStatusCode) {
|
if (response.IsSuccessStatusCode) {
|
||||||
Dictionary<string, object> ret = JsonConvert.DeserializeObject<Dictionary<string, object>>(resultString, new LeanCloudJsonConverter());
|
T ret = JsonConvert.DeserializeObject<T>(resultString, new LeanCloudJsonConverter());
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
int code = (int)statusCode;
|
int code = (int)statusCode;
|
||||||
|
|
@ -109,7 +114,7 @@ namespace LeanCloud.Storage.Internal.Http {
|
||||||
try {
|
try {
|
||||||
// 尝试获取 LeanCloud 返回错误信息
|
// 尝试获取 LeanCloud 返回错误信息
|
||||||
Dictionary<string, object> error = JsonConvert.DeserializeObject<Dictionary<string, object>>(resultString, new LeanCloudJsonConverter());
|
Dictionary<string, object> error = JsonConvert.DeserializeObject<Dictionary<string, object>>(resultString, new LeanCloudJsonConverter());
|
||||||
code = (int)error["code"];
|
code = (int)(long)error["code"];
|
||||||
message = error["error"].ToString();
|
message = error["error"].ToString();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Logger.Error(e.Message);
|
Logger.Error(e.Message);
|
||||||
|
|
@ -118,13 +123,19 @@ namespace LeanCloud.Storage.Internal.Http {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<Dictionary<string, object>> Put(string path,
|
internal async Task<T> Put<T>(string path,
|
||||||
Dictionary<string, object> headers = null,
|
Dictionary<string, object> headers = null,
|
||||||
Dictionary<string, object> data = null,
|
Dictionary<string, object> data = null,
|
||||||
Dictionary<string, object> queryParams = null) {
|
Dictionary<string, object> queryParams = null) {
|
||||||
|
string url = $"{server}/{apiVersion}/{path}";
|
||||||
|
if (queryParams != null) {
|
||||||
|
IEnumerable<string> queryPairs = queryParams.Select(kv => $"{kv.Key}={kv.Value}");
|
||||||
|
string queries = string.Join("&", queryPairs);
|
||||||
|
url = $"{url}?{queries}";
|
||||||
|
}
|
||||||
string content = (data != null) ? JsonConvert.SerializeObject(data) : null;
|
string content = (data != null) ? JsonConvert.SerializeObject(data) : null;
|
||||||
HttpRequestMessage request = new HttpRequestMessage {
|
HttpRequestMessage request = new HttpRequestMessage {
|
||||||
RequestUri = new Uri($"{server}/{apiVersion}/{path}"),
|
RequestUri = new Uri(url),
|
||||||
Method = HttpMethod.Put,
|
Method = HttpMethod.Put,
|
||||||
Content = new StringContent(content)
|
Content = new StringContent(content)
|
||||||
};
|
};
|
||||||
|
|
@ -139,11 +150,43 @@ namespace LeanCloud.Storage.Internal.Http {
|
||||||
|
|
||||||
HttpStatusCode statusCode = response.StatusCode;
|
HttpStatusCode statusCode = response.StatusCode;
|
||||||
if (response.IsSuccessStatusCode) {
|
if (response.IsSuccessStatusCode) {
|
||||||
Dictionary<string, object> ret = JsonConvert.DeserializeObject<Dictionary<string, object>>(resultString, new LeanCloudJsonConverter());
|
T ret = JsonConvert.DeserializeObject<T>(resultString, new LeanCloudJsonConverter());
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
int code = (int)statusCode;
|
int code = (int)statusCode;
|
||||||
string message = resultString;
|
string message = resultString;
|
||||||
|
try {
|
||||||
|
// 尝试获取 LeanCloud 返回错误信息
|
||||||
|
Dictionary<string, object> error = JsonConvert.DeserializeObject<Dictionary<string, object>>(resultString, new LeanCloudJsonConverter());
|
||||||
|
code = (int)(long)error["code"];
|
||||||
|
message = error["error"].ToString();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logger.Error(e.Message);
|
||||||
|
} finally {
|
||||||
|
throw new LCException(code, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal async Task Delete(string path) {
|
||||||
|
HttpRequestMessage request = new HttpRequestMessage {
|
||||||
|
RequestUri = new Uri($"{server}/{apiVersion}/{path}"),
|
||||||
|
Method = HttpMethod.Delete
|
||||||
|
};
|
||||||
|
HttpUtils.PrintRequest(client, request);
|
||||||
|
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<string, object> ret = JsonConvert.DeserializeObject<Dictionary<string, object>>(resultString, new LeanCloudJsonConverter());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int code = (int)statusCode;
|
||||||
|
string message = resultString;
|
||||||
try {
|
try {
|
||||||
// 尝试获取 LeanCloud 返回错误信息
|
// 尝试获取 LeanCloud 返回错误信息
|
||||||
Dictionary<string, object> error = JsonConvert.DeserializeObject<Dictionary<string, object>>(resultString, new LeanCloudJsonConverter());
|
Dictionary<string, object> error = JsonConvert.DeserializeObject<Dictionary<string, object>>(resultString, new LeanCloudJsonConverter());
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ namespace LeanCloud.Storage.Internal.Operation {
|
||||||
internal interface ILCOperation {
|
internal interface ILCOperation {
|
||||||
ILCOperation MergeWithPrevious(ILCOperation previousOp);
|
ILCOperation MergeWithPrevious(ILCOperation previousOp);
|
||||||
|
|
||||||
Dictionary<string, object> Encode();
|
object Encode();
|
||||||
|
|
||||||
object Apply(object oldValue, string key);
|
object Apply(object oldValue, string key);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ namespace LeanCloud.Storage.Internal.Operation {
|
||||||
throw new ArgumentException("Operation is invalid after previous operation.");
|
throw new ArgumentException("Operation is invalid after previous operation.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Dictionary<string, object> ILCOperation.Encode() {
|
object ILCOperation.Encode() {
|
||||||
return new Dictionary<string, object> {
|
return new Dictionary<string, object> {
|
||||||
{ "op", "Add" },
|
{ "op", "Add" },
|
||||||
{ "objects", LCEncoder.Encode(valueList) }
|
{ "objects", LCEncoder.Encode(valueList) }
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ namespace LeanCloud.Storage.Internal.Operation {
|
||||||
throw new ArgumentException("Operation is invalid after previous operation.");
|
throw new ArgumentException("Operation is invalid after previous operation.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Dictionary<string, object> Encode() {
|
public object Encode() {
|
||||||
return new Dictionary<string, object> {
|
return new Dictionary<string, object> {
|
||||||
{ "__op", "AddRelation" },
|
{ "__op", "AddRelation" },
|
||||||
{ "objects", LCEncoder.Encode(valueList) }
|
{ "objects", LCEncoder.Encode(valueList) }
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ namespace LeanCloud.Storage.Internal.Operation {
|
||||||
throw new ArgumentException("Operation is invalid after previous operation.");
|
throw new ArgumentException("Operation is invalid after previous operation.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Dictionary<string, object> Encode() {
|
public object Encode() {
|
||||||
return new Dictionary<string, object> {
|
return new Dictionary<string, object> {
|
||||||
{ "__op", "AddUnique" },
|
{ "__op", "AddUnique" },
|
||||||
{ "objects", LCEncoder.Encode(values.ToList()) }
|
{ "objects", LCEncoder.Encode(values.ToList()) }
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ namespace LeanCloud.Storage.Internal.Operation {
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Dictionary<string, object> Encode() {
|
public object Encode() {
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ namespace LeanCloud.Storage.Internal.Operation {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Dictionary<string, object> Encode() {
|
public object Encode() {
|
||||||
return new Dictionary<string, object> {
|
return new Dictionary<string, object> {
|
||||||
{ "__op", "Delete" }
|
{ "__op", "Delete" }
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ namespace LeanCloud.Storage.Internal.Operation {
|
||||||
throw new ArgumentException("Operation is invalid after previous operation.");
|
throw new ArgumentException("Operation is invalid after previous operation.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Dictionary<string, object> Encode() {
|
public object Encode() {
|
||||||
return new Dictionary<string, object> {
|
return new Dictionary<string, object> {
|
||||||
{ "__op", "Remove" },
|
{ "__op", "Remove" },
|
||||||
{ "objects", LCEncoder.Encode(valueList) }
|
{ "objects", LCEncoder.Encode(valueList) }
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ namespace LeanCloud.Storage.Internal.Operation {
|
||||||
throw new ArgumentException("Operation is invalid after previous operation.");
|
throw new ArgumentException("Operation is invalid after previous operation.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Dictionary<string, object> Encode() {
|
public object Encode() {
|
||||||
return new Dictionary<string, object> {
|
return new Dictionary<string, object> {
|
||||||
{ "__op", "RemoveRelation" },
|
{ "__op", "RemoveRelation" },
|
||||||
{ "objects", LCEncoder.Encode(valueList) }
|
{ "objects", LCEncoder.Encode(valueList) }
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@ namespace LeanCloud.Storage.Internal.Operation {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Dictionary<string, object> Encode() {
|
public object Encode() {
|
||||||
return LCEncoder.Encode(value) as Dictionary<string, object>;
|
return LCEncoder.Encode(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public object Apply(object oldValue, string key) {
|
public object Apply(object oldValue, string key) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using LeanCloud.Storage.Internal.Codec;
|
using LeanCloud.Storage.Internal.Codec;
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal.Query {
|
namespace LeanCloud.Storage.Internal.Query {
|
||||||
|
|
@ -189,9 +190,7 @@ namespace LeanCloud.Storage.Internal.Query {
|
||||||
if (conditionList == null || conditionList.Count == 0) {
|
if (conditionList == null || conditionList.Count == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// TODO
|
return JsonConvert.SerializeObject(Encode());
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ namespace LeanCloud.Storage {
|
||||||
LCObject obj = new LCObject(className);
|
LCObject obj = new LCObject(className);
|
||||||
obj.data.ObjectId = objectId;
|
obj.data.ObjectId = objectId;
|
||||||
obj.isNew = false;
|
obj.isNew = false;
|
||||||
return null;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static LCObject Create(string className) {
|
internal static LCObject Create(string className) {
|
||||||
|
|
@ -106,13 +106,15 @@ namespace LeanCloud.Storage {
|
||||||
|
|
||||||
public object this[string key] {
|
public object this[string key] {
|
||||||
get {
|
get {
|
||||||
object value = estimatedData[key];
|
if (estimatedData.TryGetValue(key, out object value)) {
|
||||||
if (value is LCRelation<LCObject> relation) {
|
if (value is LCRelation<LCObject> relation) {
|
||||||
relation.Key = key;
|
relation.Key = key;
|
||||||
relation.Parent = this;
|
relation.Parent = this;
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
set {
|
set {
|
||||||
if (string.IsNullOrEmpty(key)) {
|
if (string.IsNullOrEmpty(key)) {
|
||||||
throw new ArgumentNullException(nameof(key));
|
throw new ArgumentNullException(nameof(key));
|
||||||
|
|
@ -161,11 +163,13 @@ namespace LeanCloud.Storage {
|
||||||
|
|
||||||
List<Dictionary<string, object>> results = await LeanCloud.HttpClient.Post<List<Dictionary<string, object>>>("batch", data: data);
|
List<Dictionary<string, object>> results = await LeanCloud.HttpClient.Post<List<Dictionary<string, object>>>("batch", data: data);
|
||||||
List<LCObjectData> resultList = results.Select(item => {
|
List<LCObjectData> resultList = results.Select(item => {
|
||||||
if (item.TryGetValue("error", out object message)) {
|
if (item.TryGetValue("error", out object error)) {
|
||||||
int code = (int)item["code"];
|
Dictionary<string, object> err = error as Dictionary<string, object>;
|
||||||
|
int code = (int)err["code"];
|
||||||
|
string message = (string)err["error"];
|
||||||
throw new LCException(code, message as string);
|
throw new LCException(code, message as string);
|
||||||
}
|
}
|
||||||
return LCObjectData.Decode(item);
|
return LCObjectData.Decode(item["success"] as IDictionary);
|
||||||
}).ToList();
|
}).ToList();
|
||||||
|
|
||||||
for (int i = 0; i < dirtyObjects.Count; i++) {
|
for (int i = 0; i < dirtyObjects.Count; i++) {
|
||||||
|
|
@ -196,12 +200,68 @@ namespace LeanCloud.Storage {
|
||||||
}
|
}
|
||||||
Dictionary<string, object> response = ObjectId == null ?
|
Dictionary<string, object> response = ObjectId == null ?
|
||||||
await LeanCloud.HttpClient.Post<Dictionary<string, object>>(path, data: LCEncoder.Encode(operationDict) as Dictionary<string, object>, queryParams: queryParams) :
|
await LeanCloud.HttpClient.Post<Dictionary<string, object>>(path, data: LCEncoder.Encode(operationDict) as Dictionary<string, object>, queryParams: queryParams) :
|
||||||
await LeanCloud.HttpClient.Put(path, data: LCEncoder.Encode(operationDict) as Dictionary<string, object>, queryParams: queryParams);
|
await LeanCloud.HttpClient.Put<Dictionary<string, object>>(path, data: LCEncoder.Encode(operationDict) as Dictionary<string, object>, queryParams: queryParams);
|
||||||
LCObjectData data = LCObjectData.Decode(response);
|
LCObjectData data = LCObjectData.Decode(response);
|
||||||
Merge(data);
|
Merge(data);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task<List<LCObject>> SaveAll(List<LCObject> objectList) {
|
||||||
|
if (objectList == null) {
|
||||||
|
throw new ArgumentNullException(nameof(objectList));
|
||||||
|
}
|
||||||
|
foreach (LCObject obj in objectList) {
|
||||||
|
if (LCBatch.HasCircleReference(obj, new HashSet<LCObject>())) {
|
||||||
|
throw new ArgumentException("Found a circle dependency when save.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Stack<LCBatch> batches = LCBatch.BatchObjects(objectList, true);
|
||||||
|
await SaveBatches(batches);
|
||||||
|
return objectList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Delete() {
|
||||||
|
if (ObjectId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
string path = $"classes/{ClassName}/{ObjectId}";
|
||||||
|
await LeanCloud.HttpClient.Delete(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task DeleteAll(List<LCObject> objectList) {
|
||||||
|
if (objectList == null || objectList.Count == 0) {
|
||||||
|
throw new ArgumentNullException(nameof(objectList));
|
||||||
|
}
|
||||||
|
IEnumerable<LCObject> objects = objectList.Where(item => item.ObjectId != null);
|
||||||
|
HashSet<LCObject> objectSet = new HashSet<LCObject>(objects);
|
||||||
|
List<Dictionary<string, object>> requestList = objectSet.Select(item => {
|
||||||
|
string path = $"/{LeanCloud.APIVersion}/classes/{item.ClassName}/{item.ObjectId}";
|
||||||
|
return new Dictionary<string, object> {
|
||||||
|
{ "path", path },
|
||||||
|
{ "method", "DELETE" }
|
||||||
|
};
|
||||||
|
}).ToList();
|
||||||
|
Dictionary<string, object> data = new Dictionary<string, object> {
|
||||||
|
{ "requests", LCEncoder.Encode(requestList) }
|
||||||
|
};
|
||||||
|
await LeanCloud.HttpClient.Post<List<object>>("batch", data: data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LCObject> Fetch(IEnumerable<string> keys = null, IEnumerable<string> includes = null) {
|
||||||
|
Dictionary<string, object> queryParams = new Dictionary<string, object>();
|
||||||
|
if (keys != null) {
|
||||||
|
queryParams["keys"] = string.Join(",", keys);
|
||||||
|
}
|
||||||
|
if (includes != null) {
|
||||||
|
queryParams["include"] = string.Join(",", includes);
|
||||||
|
}
|
||||||
|
string path = $"classes/{ClassName}/{ObjectId}";
|
||||||
|
Dictionary<string, object> response = await LeanCloud.HttpClient.Get<Dictionary<string, object>>(path, queryParams: queryParams);
|
||||||
|
LCObjectData objectData = LCObjectData.Decode(response);
|
||||||
|
Merge(objectData);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public static void RegisterSubclass(string className, Type type, Func<LCObject> constructor) {
|
public static void RegisterSubclass(string className, Type type, Func<LCObject> constructor) {
|
||||||
LCSubclassInfo subclassInfo = new LCSubclassInfo(className, type, constructor);
|
LCSubclassInfo subclassInfo = new LCSubclassInfo(className, type, constructor);
|
||||||
subclassNameDict[className] = subclassInfo;
|
subclassNameDict[className] = subclassInfo;
|
||||||
|
|
@ -217,8 +277,11 @@ namespace LeanCloud.Storage {
|
||||||
if (op is LCDeleteOperation) {
|
if (op is LCDeleteOperation) {
|
||||||
estimatedData.Remove(key);
|
estimatedData.Remove(key);
|
||||||
} else {
|
} else {
|
||||||
object oldValue = estimatedData[key];
|
if (estimatedData.TryGetValue(key, out object oldValue)) {
|
||||||
estimatedData[key] = op.Apply(oldValue, key);
|
estimatedData[key] = op.Apply(oldValue, key);
|
||||||
|
} else {
|
||||||
|
estimatedData[key] = op.Apply(null, key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -274,7 +274,7 @@ namespace LeanCloud.Storage {
|
||||||
Dictionary<string, object> parameters = BuildParams();
|
Dictionary<string, object> parameters = BuildParams();
|
||||||
parameters["limit"] = 0;
|
parameters["limit"] = 0;
|
||||||
parameters["count"] = 1;
|
parameters["count"] = 1;
|
||||||
Dictionary<string, object> ret = await LeanCloud.HttpClient.Get(path, queryParams: parameters);
|
Dictionary<string, object> ret = await LeanCloud.HttpClient.Get<Dictionary<string, object>>(path, queryParams: parameters);
|
||||||
return (int)ret["count"];
|
return (int)ret["count"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -297,7 +297,7 @@ namespace LeanCloud.Storage {
|
||||||
public async Task<List<T>> Find() {
|
public async Task<List<T>> Find() {
|
||||||
string path = $"classes/{ClassName}";
|
string path = $"classes/{ClassName}";
|
||||||
Dictionary<string, object> parameters = BuildParams();
|
Dictionary<string, object> parameters = BuildParams();
|
||||||
Dictionary<string, object> response = await LeanCloud.HttpClient.Get(path, queryParams: parameters);
|
Dictionary<string, object> response = await LeanCloud.HttpClient.Get<Dictionary<string, object>>(path, queryParams: parameters);
|
||||||
List<object> results = response["results"] as List<object>;
|
List<object> results = response["results"] as List<object>;
|
||||||
List<T> list = new List<T>();
|
List<T> list = new List<T>();
|
||||||
foreach (object item in results) {
|
foreach (object item in results) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue