chore: 简化
parent
905cc943bf
commit
81b2ea993f
|
@ -1,65 +1,45 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
namespace LeanCloud.Storage.Internal {
|
||||||
using System.Linq;
|
public class AVPlugins {
|
||||||
using System.Text;
|
|
||||||
using LeanCloud.Storage.Internal;
|
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal
|
|
||||||
{
|
|
||||||
public class AVPlugins : IAVCorePlugins
|
|
||||||
{
|
|
||||||
private static readonly object instanceMutex = new object();
|
private static readonly object instanceMutex = new object();
|
||||||
private static IAVCorePlugins instance;
|
private static AVPlugins instance;
|
||||||
public static IAVCorePlugins Instance
|
public static AVPlugins Instance {
|
||||||
{
|
get {
|
||||||
get
|
lock (instanceMutex) {
|
||||||
{
|
|
||||||
lock (instanceMutex)
|
|
||||||
{
|
|
||||||
instance = instance ?? new AVPlugins();
|
instance = instance ?? new AVPlugins();
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set
|
|
||||||
{
|
|
||||||
lock (instanceMutex)
|
|
||||||
{
|
|
||||||
instance = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly object mutex = new object();
|
private readonly object mutex = new object();
|
||||||
|
|
||||||
#region Server Controllers
|
#region Server Controllers
|
||||||
|
|
||||||
private IHttpClient httpClient;
|
private HttpClient httpClient;
|
||||||
private IAppRouterController appRouterController;
|
private AppRouterController appRouterController;
|
||||||
private IAVCommandRunner commandRunner;
|
private AVCommandRunner commandRunner;
|
||||||
private IStorageController storageController;
|
private StorageController storageController;
|
||||||
|
|
||||||
private IAVCloudCodeController cloudCodeController;
|
private AVCloudCodeController cloudCodeController;
|
||||||
private IAVConfigController configController;
|
private AVFileController fileController;
|
||||||
private IAVFileController fileController;
|
private AVObjectController objectController;
|
||||||
private IAVObjectController objectController;
|
private AVQueryController queryController;
|
||||||
private IAVQueryController queryController;
|
private AVSessionController sessionController;
|
||||||
private IAVSessionController sessionController;
|
private AVUserController userController;
|
||||||
private IAVUserController userController;
|
private ObjectSubclassingController subclassingController;
|
||||||
private IObjectSubclassingController subclassingController;
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Current Instance Controller
|
#region Current Instance Controller
|
||||||
|
|
||||||
private IAVCurrentUserController currentUserController;
|
private AVCurrentUserController currentUserController;
|
||||||
private IInstallationIdController installationIdController;
|
private InstallationIdController installationIdController;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public void Reset()
|
public void Reset() {
|
||||||
{
|
lock (mutex) {
|
||||||
lock (mutex)
|
|
||||||
{
|
|
||||||
HttpClient = null;
|
HttpClient = null;
|
||||||
AppRouterController = null;
|
AppRouterController = null;
|
||||||
CommandRunner = null;
|
CommandRunner = null;
|
||||||
|
@ -77,305 +57,217 @@ namespace LeanCloud.Storage.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IHttpClient HttpClient
|
public HttpClient HttpClient {
|
||||||
{
|
get {
|
||||||
get
|
lock (mutex) {
|
||||||
{
|
|
||||||
lock (mutex)
|
|
||||||
{
|
|
||||||
httpClient = httpClient ?? new HttpClient();
|
httpClient = httpClient ?? new HttpClient();
|
||||||
return httpClient;
|
return httpClient;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set
|
set {
|
||||||
{
|
lock (mutex) {
|
||||||
lock (mutex)
|
|
||||||
{
|
|
||||||
httpClient = value;
|
httpClient = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IAppRouterController AppRouterController
|
public AppRouterController AppRouterController {
|
||||||
{
|
get {
|
||||||
get
|
lock (mutex) {
|
||||||
{
|
|
||||||
lock (mutex)
|
|
||||||
{
|
|
||||||
appRouterController = appRouterController ?? new AppRouterController();
|
appRouterController = appRouterController ?? new AppRouterController();
|
||||||
return appRouterController;
|
return appRouterController;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set
|
set {
|
||||||
{
|
lock (mutex) {
|
||||||
lock (mutex)
|
|
||||||
{
|
|
||||||
appRouterController = value;
|
appRouterController = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IAVCommandRunner CommandRunner
|
public AVCommandRunner CommandRunner {
|
||||||
{
|
get {
|
||||||
get
|
lock (mutex) {
|
||||||
{
|
commandRunner = commandRunner ?? new AVCommandRunner();
|
||||||
lock (mutex)
|
|
||||||
{
|
|
||||||
commandRunner = commandRunner ?? new AVCommandRunner(HttpClient, InstallationIdController);
|
|
||||||
return commandRunner;
|
return commandRunner;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set
|
set {
|
||||||
{
|
lock (mutex) {
|
||||||
lock (mutex)
|
|
||||||
{
|
|
||||||
commandRunner = value;
|
commandRunner = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !UNITY
|
|
||||||
public IStorageController StorageController
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
lock (mutex)
|
|
||||||
{
|
|
||||||
storageController = storageController ?? new StorageController(AVClient.CurrentConfiguration.ApplicationId);
|
|
||||||
return storageController;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
lock (mutex)
|
|
||||||
{
|
|
||||||
storageController = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#if UNITY
|
#if UNITY
|
||||||
public IStorageController StorageController
|
public StorageController StorageController {
|
||||||
{
|
get {
|
||||||
get
|
lock (mutex) {
|
||||||
{
|
|
||||||
lock (mutex)
|
|
||||||
{
|
|
||||||
storageController = storageController ?? new StorageController(AVInitializeBehaviour.IsWebPlayer, AVClient.CurrentConfiguration.ApplicationId);
|
storageController = storageController ?? new StorageController(AVInitializeBehaviour.IsWebPlayer, AVClient.CurrentConfiguration.ApplicationId);
|
||||||
return storageController;
|
return storageController;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set
|
set {
|
||||||
{
|
lock (mutex) {
|
||||||
lock (mutex)
|
storageController = value;
|
||||||
{
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
public StorageController StorageController {
|
||||||
|
get {
|
||||||
|
lock (mutex) {
|
||||||
|
storageController = storageController ?? new StorageController(AVClient.CurrentConfiguration.ApplicationId);
|
||||||
|
return storageController;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
lock (mutex) {
|
||||||
storageController = value;
|
storageController = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public IAVCloudCodeController CloudCodeController
|
public AVCloudCodeController CloudCodeController {
|
||||||
{
|
get {
|
||||||
get
|
lock (mutex) {
|
||||||
{
|
cloudCodeController = cloudCodeController ?? new AVCloudCodeController();
|
||||||
lock (mutex)
|
|
||||||
{
|
|
||||||
cloudCodeController = cloudCodeController ?? new AVCloudCodeController(CommandRunner);
|
|
||||||
return cloudCodeController;
|
return cloudCodeController;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set
|
set {
|
||||||
{
|
lock (mutex) {
|
||||||
lock (mutex)
|
|
||||||
{
|
|
||||||
cloudCodeController = value;
|
cloudCodeController = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IAVFileController FileController
|
public AVFileController FileController {
|
||||||
{
|
get {
|
||||||
get
|
if (fileController != null) {
|
||||||
{
|
return fileController;
|
||||||
lock (mutex)
|
}
|
||||||
{
|
lock (mutex) {
|
||||||
if (AVClient.CurrentConfiguration.RegionValue == 0)
|
switch (AVClient.CurrentConfiguration.RegionValue) {
|
||||||
fileController = fileController ?? new QiniuFileController(CommandRunner);
|
case 0:
|
||||||
else if (AVClient.CurrentConfiguration.RegionValue == 2)
|
fileController = new QiniuFileController();
|
||||||
fileController = fileController ?? new QCloudCosFileController(CommandRunner);
|
break;
|
||||||
else if (AVClient.CurrentConfiguration.RegionValue == 1)
|
case 2:
|
||||||
fileController = fileController ?? new AWSS3FileController(CommandRunner);
|
fileController = new QCloudCosFileController();
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
fileController = new AWSS3FileController();
|
||||||
|
break;
|
||||||
|
}
|
||||||
return fileController;
|
return fileController;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set
|
set {
|
||||||
{
|
lock (mutex) {
|
||||||
lock (mutex)
|
|
||||||
{
|
|
||||||
fileController = value;
|
fileController = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IAVConfigController ConfigController
|
public AVObjectController ObjectController {
|
||||||
{
|
get {
|
||||||
get
|
lock (mutex) {
|
||||||
{
|
objectController = objectController ?? new AVObjectController();
|
||||||
lock (mutex)
|
|
||||||
{
|
|
||||||
if (configController == null)
|
|
||||||
{
|
|
||||||
configController = new AVConfigController(CommandRunner, StorageController);
|
|
||||||
}
|
|
||||||
return configController;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
lock (mutex)
|
|
||||||
{
|
|
||||||
configController = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IAVObjectController ObjectController
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
lock (mutex)
|
|
||||||
{
|
|
||||||
objectController = objectController ?? new AVObjectController(CommandRunner);
|
|
||||||
return objectController;
|
return objectController;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set
|
set {
|
||||||
{
|
lock (mutex) {
|
||||||
lock (mutex)
|
|
||||||
{
|
|
||||||
objectController = value;
|
objectController = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IAVQueryController QueryController
|
public AVQueryController QueryController {
|
||||||
{
|
get {
|
||||||
get
|
lock (mutex) {
|
||||||
{
|
if (queryController == null) {
|
||||||
lock (mutex)
|
queryController = new AVQueryController();
|
||||||
{
|
|
||||||
if (queryController == null)
|
|
||||||
{
|
|
||||||
queryController = new AVQueryController(CommandRunner);
|
|
||||||
}
|
}
|
||||||
return queryController;
|
return queryController;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set
|
set {
|
||||||
{
|
lock (mutex) {
|
||||||
lock (mutex)
|
|
||||||
{
|
|
||||||
queryController = value;
|
queryController = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IAVSessionController SessionController
|
public AVSessionController SessionController {
|
||||||
{
|
get {
|
||||||
get
|
lock (mutex) {
|
||||||
{
|
sessionController = sessionController ?? new AVSessionController();
|
||||||
lock (mutex)
|
|
||||||
{
|
|
||||||
sessionController = sessionController ?? new AVSessionController(CommandRunner);
|
|
||||||
return sessionController;
|
return sessionController;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set
|
set {
|
||||||
{
|
lock (mutex) {
|
||||||
lock (mutex)
|
|
||||||
{
|
|
||||||
sessionController = value;
|
sessionController = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IAVUserController UserController
|
public AVUserController UserController {
|
||||||
{
|
get {
|
||||||
get
|
lock (mutex) {
|
||||||
{
|
userController = userController ?? new AVUserController();
|
||||||
lock (mutex)
|
|
||||||
{
|
|
||||||
userController = userController ?? new AVUserController(CommandRunner);
|
|
||||||
return userController;
|
return userController;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set
|
set {
|
||||||
{
|
lock (mutex) {
|
||||||
lock (mutex)
|
|
||||||
{
|
|
||||||
userController = value;
|
userController = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IAVCurrentUserController CurrentUserController
|
public AVCurrentUserController CurrentUserController {
|
||||||
{
|
get {
|
||||||
get
|
lock (mutex) {
|
||||||
{
|
currentUserController = currentUserController ?? new AVCurrentUserController();
|
||||||
lock (mutex)
|
|
||||||
{
|
|
||||||
currentUserController = currentUserController ?? new AVCurrentUserController(StorageController);
|
|
||||||
return currentUserController;
|
return currentUserController;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set
|
set {
|
||||||
{
|
lock (mutex) {
|
||||||
lock (mutex)
|
|
||||||
{
|
|
||||||
currentUserController = value;
|
currentUserController = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IObjectSubclassingController SubclassingController
|
public ObjectSubclassingController SubclassingController {
|
||||||
{
|
get {
|
||||||
get
|
lock (mutex) {
|
||||||
{
|
if (subclassingController == null) {
|
||||||
lock (mutex)
|
|
||||||
{
|
|
||||||
if (subclassingController == null)
|
|
||||||
{
|
|
||||||
subclassingController = new ObjectSubclassingController();
|
subclassingController = new ObjectSubclassingController();
|
||||||
subclassingController.AddRegisterHook(typeof(AVUser), () => CurrentUserController.ClearFromMemory());
|
subclassingController.AddRegisterHook(typeof(AVUser), () => CurrentUserController.ClearFromMemory());
|
||||||
}
|
}
|
||||||
return subclassingController;
|
return subclassingController;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set
|
set {
|
||||||
{
|
lock (mutex) {
|
||||||
lock (mutex)
|
|
||||||
{
|
|
||||||
subclassingController = value;
|
subclassingController = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IInstallationIdController InstallationIdController
|
public InstallationIdController InstallationIdController {
|
||||||
{
|
get {
|
||||||
get
|
lock (mutex) {
|
||||||
{
|
installationIdController = installationIdController ?? new InstallationIdController();
|
||||||
lock (mutex)
|
|
||||||
{
|
|
||||||
installationIdController = installationIdController ?? new InstallationIdController(StorageController);
|
|
||||||
return installationIdController;
|
return installationIdController;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set
|
set {
|
||||||
{
|
lock (mutex) {
|
||||||
lock (mutex)
|
|
||||||
{
|
|
||||||
installationIdController = value;
|
installationIdController = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,17 +6,11 @@ using System.Threading.Tasks;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal
|
namespace LeanCloud.Storage.Internal {
|
||||||
{
|
public class AppRouterController {
|
||||||
public class AppRouterController : IAppRouterController
|
|
||||||
{
|
|
||||||
private AppRouterState currentState;
|
private AppRouterState currentState;
|
||||||
private readonly ReaderWriterLockSlim locker = new ReaderWriterLockSlim();
|
private readonly ReaderWriterLockSlim locker = new ReaderWriterLockSlim();
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get current app's router state
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public AppRouterState Get() {
|
public AppRouterState Get() {
|
||||||
if (string.IsNullOrEmpty(AVClient.CurrentConfiguration.ApplicationId)) {
|
if (string.IsNullOrEmpty(AVClient.CurrentConfiguration.ApplicationId)) {
|
||||||
throw new AVException(AVException.ErrorCode.NotInitialized, "ApplicationId can not be null.");
|
throw new AVException(AVException.ErrorCode.NotInitialized, "ApplicationId can not be null.");
|
||||||
|
|
|
@ -47,21 +47,12 @@ namespace LeanCloud.Storage.Internal
|
||||||
FetchedAt = DateTime.Now;
|
FetchedAt = DateTime.Now;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Is this app router state expired.
|
|
||||||
/// </summary>
|
|
||||||
public bool IsExpired {
|
public bool IsExpired {
|
||||||
get {
|
get {
|
||||||
return DateTime.Now > FetchedAt + TimeSpan.FromSeconds(TTL);
|
return DateTime.Now > FetchedAt + TimeSpan.FromSeconds(TTL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get the initial usable router state
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="appId">Current app's appId</param>
|
|
||||||
/// <param name="region">Current app's region</param>
|
|
||||||
/// <returns>Initial app router state</returns>
|
|
||||||
public static AppRouterState GetFallbackServers(string appId, AVClient.Configuration.AVRegion region) {
|
public static AppRouterState GetFallbackServers(string appId, AVClient.Configuration.AVRegion region) {
|
||||||
var regionValue = (int)region;
|
var regionValue = (int)region;
|
||||||
var prefix = appId.Substring(0, 8).ToLower();
|
var prefix = appId.Substring(0, 8).ToLower();
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal
|
|
||||||
{
|
|
||||||
public interface IAppRouterController
|
|
||||||
{
|
|
||||||
AppRouterState Get();
|
|
||||||
Task<AppRouterState> QueryAsync(CancellationToken cancellationToken);
|
|
||||||
void Clear();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -7,15 +7,8 @@ using System.Net.Http;
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal
|
namespace LeanCloud.Storage.Internal
|
||||||
{
|
{
|
||||||
public class AVCloudCodeController : IAVCloudCodeController
|
public class AVCloudCodeController
|
||||||
{
|
{
|
||||||
private readonly IAVCommandRunner commandRunner;
|
|
||||||
|
|
||||||
public AVCloudCodeController(IAVCommandRunner commandRunner)
|
|
||||||
{
|
|
||||||
this.commandRunner = commandRunner;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<T> CallFunctionAsync<T>(String name,
|
public Task<T> CallFunctionAsync<T>(String name,
|
||||||
IDictionary<string, object> parameters,
|
IDictionary<string, object> parameters,
|
||||||
string sessionToken,
|
string sessionToken,
|
||||||
|
@ -26,7 +19,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
Method = HttpMethod.Post,
|
Method = HttpMethod.Post,
|
||||||
Content = parameters
|
Content = parameters
|
||||||
};
|
};
|
||||||
return commandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t =>
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t =>
|
||||||
{
|
{
|
||||||
var decoded = AVDecoder.Instance.Decode(t.Result.Item2) as IDictionary<string, object>;
|
var decoded = AVDecoder.Instance.Decode(t.Result.Item2) as IDictionary<string, object>;
|
||||||
if (!decoded.ContainsKey("result"))
|
if (!decoded.ContainsKey("result"))
|
||||||
|
@ -44,7 +37,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
Method = HttpMethod.Post,
|
Method = HttpMethod.Post,
|
||||||
Content = parameters
|
Content = parameters
|
||||||
};
|
};
|
||||||
return commandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t =>
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t =>
|
||||||
{
|
{
|
||||||
var decoded = AVDecoder.Instance.Decode(t.Result.Item2) as IDictionary<string, object>;
|
var decoded = AVDecoder.Instance.Decode(t.Result.Item2) as IDictionary<string, object>;
|
||||||
if (!decoded.ContainsKey("result"))
|
if (!decoded.ContainsKey("result"))
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal
|
|
||||||
{
|
|
||||||
public interface IAVCloudCodeController
|
|
||||||
{
|
|
||||||
Task<T> CallFunctionAsync<T>(String name,
|
|
||||||
IDictionary<string, object> parameters,
|
|
||||||
string sessionToken,
|
|
||||||
CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
Task<T> RPCFunction<T>(string name, IDictionary<string, object> parameters,
|
|
||||||
string sessionToken,
|
|
||||||
CancellationToken cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -7,29 +7,15 @@ using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal
|
namespace LeanCloud.Storage.Internal {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Command Runner.
|
/// Command Runner.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AVCommandRunner : IAVCommandRunner {
|
public class AVCommandRunner {
|
||||||
public const string APPLICATION_JSON = "application/json";
|
public const string APPLICATION_JSON = "application/json";
|
||||||
|
|
||||||
private readonly System.Net.Http.HttpClient httpClient;
|
private readonly System.Net.Http.HttpClient httpClient = new System.Net.Http.HttpClient();
|
||||||
private readonly IInstallationIdController installationIdController;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="httpClient"></param>
|
|
||||||
/// <param name="installationIdController"></param>
|
|
||||||
public AVCommandRunner(IHttpClient httpClient, IInstallationIdController installationIdController)
|
|
||||||
{
|
|
||||||
this.httpClient = new System.Net.Http.HttpClient();
|
|
||||||
this.installationIdController = installationIdController;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
|
@ -43,7 +29,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
IProgress<AVUploadProgressEventArgs> uploadProgress = null,
|
IProgress<AVUploadProgressEventArgs> uploadProgress = null,
|
||||||
IProgress<AVDownloadProgressEventArgs> downloadProgress = null,
|
IProgress<AVDownloadProgressEventArgs> downloadProgress = null,
|
||||||
CancellationToken cancellationToken = default) {
|
CancellationToken cancellationToken = default) {
|
||||||
|
|
||||||
var request = new HttpRequestMessage {
|
var request = new HttpRequestMessage {
|
||||||
RequestUri = command.Uri,
|
RequestUri = command.Uri,
|
||||||
Method = command.Method,
|
Method = command.Method,
|
||||||
|
@ -67,7 +53,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
PrintResponse(response, resultString);
|
PrintResponse(response, resultString);
|
||||||
|
|
||||||
var ret = new Tuple<HttpStatusCode, string>(response.StatusCode, resultString);
|
var ret = new Tuple<HttpStatusCode, string>(response.StatusCode, resultString);
|
||||||
|
|
||||||
var responseCode = ret.Item1;
|
var responseCode = ret.Item1;
|
||||||
var contentString = ret.Item2;
|
var contentString = ret.Item2;
|
||||||
|
|
||||||
|
@ -105,7 +91,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
|
|
||||||
async Task<Dictionary<string, string>> GetHeadersAsync() {
|
async Task<Dictionary<string, string>> GetHeadersAsync() {
|
||||||
var headers = new Dictionary<string, string>();
|
var headers = new Dictionary<string, string>();
|
||||||
var installationId = await installationIdController.GetAsync();
|
var installationId = await AVPlugins.Instance.InstallationIdController.GetAsync();
|
||||||
headers.Add("X-LC-Installation-Id", installationId.ToString());
|
headers.Add("X-LC-Installation-Id", installationId.ToString());
|
||||||
var conf = AVClient.CurrentConfiguration;
|
var conf = AVClient.CurrentConfiguration;
|
||||||
headers.Add("X-LC-Id", conf.ApplicationId);
|
headers.Add("X-LC-Id", conf.ApplicationId);
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Net;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal
|
|
||||||
{
|
|
||||||
public interface IAVCommandRunner
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Executes <see cref="AVCommand"/> and convert the result into Dictionary.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="command">The command to be run.</param>
|
|
||||||
/// <param name="uploadProgress">Upload progress callback.</param>
|
|
||||||
/// <param name="downloadProgress">Download progress callback.</param>
|
|
||||||
/// <param name="cancellationToken">The cancellation token for the request.</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<Tuple<HttpStatusCode, T>> RunCommandAsync<T>(AVCommand command,
|
|
||||||
IProgress<AVUploadProgressEventArgs> uploadProgress = null,
|
|
||||||
IProgress<AVDownloadProgressEventArgs> downloadProgress = null,
|
|
||||||
CancellationToken cancellationToken = default(CancellationToken));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Net.Http;
|
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal {
|
|
||||||
/// <summary>
|
|
||||||
/// Config controller.
|
|
||||||
/// </summary>
|
|
||||||
internal class AVConfigController : IAVConfigController {
|
|
||||||
private readonly IAVCommandRunner commandRunner;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="AVConfigController"/> class.
|
|
||||||
/// </summary>
|
|
||||||
public AVConfigController(IAVCommandRunner commandRunner, IStorageController storageController) {
|
|
||||||
this.commandRunner = commandRunner;
|
|
||||||
CurrentConfigController = new AVCurrentConfigController(storageController);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IAVCommandRunner CommandRunner { get; internal set; }
|
|
||||||
public IAVCurrentConfigController CurrentConfigController { get; internal set; }
|
|
||||||
|
|
||||||
public Task<AVConfig> FetchConfigAsync(String sessionToken, CancellationToken cancellationToken) {
|
|
||||||
var command = new AVCommand {
|
|
||||||
Path = "config",
|
|
||||||
Method = HttpMethod.Post,
|
|
||||||
};
|
|
||||||
|
|
||||||
return commandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(task => {
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
return new AVConfig(task.Result.Item2);
|
|
||||||
}).OnSuccess(task => {
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
CurrentConfigController.SetCurrentConfigAsync(task.Result);
|
|
||||||
return task;
|
|
||||||
}).Unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,75 +0,0 @@
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal {
|
|
||||||
/// <summary>
|
|
||||||
/// LeanCloud current config controller.
|
|
||||||
/// </summary>
|
|
||||||
internal class AVCurrentConfigController : IAVCurrentConfigController {
|
|
||||||
private const string CurrentConfigKey = "CurrentConfig";
|
|
||||||
|
|
||||||
private readonly TaskQueue taskQueue;
|
|
||||||
private AVConfig currentConfig;
|
|
||||||
|
|
||||||
private IStorageController storageController;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="LeanCloud.Storage.Internal.AVCurrentConfigController"/> class.
|
|
||||||
/// </summary>
|
|
||||||
public AVCurrentConfigController(IStorageController storageController) {
|
|
||||||
this.storageController = storageController;
|
|
||||||
|
|
||||||
taskQueue = new TaskQueue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<AVConfig> GetCurrentConfigAsync() {
|
|
||||||
return taskQueue.Enqueue(toAwait => toAwait.ContinueWith(_ => {
|
|
||||||
if (currentConfig == null) {
|
|
||||||
return storageController.LoadAsync().OnSuccess(t => {
|
|
||||||
object tmp;
|
|
||||||
t.Result.TryGetValue(CurrentConfigKey, out tmp);
|
|
||||||
|
|
||||||
string propertiesString = tmp as string;
|
|
||||||
if (propertiesString != null) {
|
|
||||||
var dictionary = JsonConvert.DeserializeObject<IDictionary<string, object>>(propertiesString);
|
|
||||||
currentConfig = new AVConfig(dictionary);
|
|
||||||
} else {
|
|
||||||
currentConfig = new AVConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
return currentConfig;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.FromResult(currentConfig);
|
|
||||||
}), CancellationToken.None).Unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task SetCurrentConfigAsync(AVConfig config) {
|
|
||||||
return taskQueue.Enqueue(toAwait => toAwait.ContinueWith(_ => {
|
|
||||||
currentConfig = config;
|
|
||||||
|
|
||||||
var jsonObject = ((IJsonConvertible)config).ToJSON();
|
|
||||||
var jsonString = JsonConvert.SerializeObject(jsonObject);
|
|
||||||
|
|
||||||
return storageController.LoadAsync().OnSuccess(t => t.Result.AddAsync(CurrentConfigKey, jsonString));
|
|
||||||
}).Unwrap().Unwrap(), CancellationToken.None);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task ClearCurrentConfigAsync() {
|
|
||||||
return taskQueue.Enqueue(toAwait => toAwait.ContinueWith(_ => {
|
|
||||||
currentConfig = null;
|
|
||||||
|
|
||||||
return storageController.LoadAsync().OnSuccess(t => t.Result.RemoveAsync(CurrentConfigKey));
|
|
||||||
}).Unwrap().Unwrap(), CancellationToken.None);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task ClearCurrentConfigInMemoryAsync() {
|
|
||||||
return taskQueue.Enqueue(toAwait => toAwait.ContinueWith(_ => {
|
|
||||||
currentConfig = null;
|
|
||||||
}), CancellationToken.None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal {
|
|
||||||
public interface IAVConfigController {
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current config controller.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The current config controller.</value>
|
|
||||||
IAVCurrentConfigController CurrentConfigController { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Fetches the config from the server asynchronously.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The config async.</returns>
|
|
||||||
/// <param name="sessionToken">Session token.</param>
|
|
||||||
/// <param name="cancellationToken">Cancellation token.</param>
|
|
||||||
Task<AVConfig> FetchConfigAsync(String sessionToken, CancellationToken cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal {
|
|
||||||
public interface IAVCurrentConfigController {
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current config async.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The current config async.</returns>
|
|
||||||
Task<AVConfig> GetCurrentConfigAsync();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the current config async.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The current config async.</returns>
|
|
||||||
/// <param name="config">Config.</param>
|
|
||||||
Task SetCurrentConfigAsync(AVConfig config);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Clears the current config async.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The current config async.</returns>
|
|
||||||
Task ClearCurrentConfigAsync();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Clears the current config in memory async.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The current config in memory async.</returns>
|
|
||||||
Task ClearCurrentConfigInMemoryAsync();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,29 +2,17 @@
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using LeanCloud.Storage.Internal;
|
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal
|
namespace LeanCloud.Storage.Internal {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// AVF ile controller.
|
/// AVF ile controller.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AVFileController : IAVFileController
|
public class AVFileController {
|
||||||
{
|
|
||||||
protected readonly IAVCommandRunner commandRunner;
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="T:LeanCloud.Storage.Internal.AVFileController"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="commandRunner">Command runner.</param>
|
|
||||||
public AVFileController(IAVCommandRunner commandRunner)
|
|
||||||
{
|
|
||||||
this.commandRunner = commandRunner;
|
|
||||||
}
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Saves the async.
|
/// Saves the async.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -65,16 +53,14 @@ namespace LeanCloud.Storage.Internal
|
||||||
MimeType = state.MimeType
|
MimeType = state.MimeType
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
public Task DeleteAsync(FileState state, string sessionToken, CancellationToken cancellationToken)
|
public Task DeleteAsync(FileState state, string sessionToken, CancellationToken cancellationToken) {
|
||||||
{
|
|
||||||
var command = new AVCommand {
|
var command = new AVCommand {
|
||||||
Path = $"files/{state.ObjectId}",
|
Path = $"files/{state.ObjectId}",
|
||||||
Method = HttpMethod.Delete
|
Method = HttpMethod.Delete
|
||||||
};
|
};
|
||||||
return commandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken);
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken);
|
||||||
}
|
}
|
||||||
internal Task<Tuple<HttpStatusCode, IDictionary<string, object>>> GetFileToken(FileState fileState, CancellationToken cancellationToken)
|
internal Task<Tuple<HttpStatusCode, IDictionary<string, object>>> GetFileToken(FileState fileState, CancellationToken cancellationToken) {
|
||||||
{
|
|
||||||
Task<Tuple<HttpStatusCode, IDictionary<string, object>>> rtn;
|
Task<Tuple<HttpStatusCode, IDictionary<string, object>>> rtn;
|
||||||
string currentSessionToken = AVUser.CurrentSessionToken;
|
string currentSessionToken = AVUser.CurrentSessionToken;
|
||||||
string str = fileState.Name;
|
string str = fileState.Name;
|
||||||
|
@ -90,44 +76,38 @@ namespace LeanCloud.Storage.Internal
|
||||||
Method = HttpMethod.Post,
|
Method = HttpMethod.Post,
|
||||||
Content = parameters
|
Content = parameters
|
||||||
};
|
};
|
||||||
return commandRunner.RunCommandAsync<IDictionary<string, object>>(command);
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command);
|
||||||
}
|
}
|
||||||
public Task<FileState> GetAsync(string objectId, string sessionToken, CancellationToken cancellationToken)
|
public Task<FileState> GetAsync(string objectId, string sessionToken, CancellationToken cancellationToken) {
|
||||||
{
|
|
||||||
var command = new AVCommand {
|
var command = new AVCommand {
|
||||||
Path = $"files/{objectId}",
|
Path = $"files/{objectId}",
|
||||||
Method = HttpMethod.Get
|
Method = HttpMethod.Get
|
||||||
};
|
};
|
||||||
return commandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(_ =>
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(_ => {
|
||||||
{
|
|
||||||
var result = _.Result;
|
var result = _.Result;
|
||||||
var jsonData = result.Item2;
|
var jsonData = result.Item2;
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
return new FileState
|
return new FileState {
|
||||||
{
|
|
||||||
ObjectId = jsonData["objectId"] as string,
|
ObjectId = jsonData["objectId"] as string,
|
||||||
Name = jsonData["name"] as string,
|
Name = jsonData["name"] as string,
|
||||||
Url = new Uri(jsonData["url"] as string, UriKind.Absolute),
|
Url = new Uri(jsonData["url"] as string, UriKind.Absolute),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
internal static string GetUniqueName(FileState fileState)
|
internal static string GetUniqueName(FileState fileState) {
|
||||||
{
|
|
||||||
string key = Random(12);
|
string key = Random(12);
|
||||||
string extension = Path.GetExtension(fileState.Name);
|
string extension = Path.GetExtension(fileState.Name);
|
||||||
key += extension;
|
key += extension;
|
||||||
fileState.CloudName = key;
|
fileState.CloudName = key;
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
internal static string Random(int length)
|
internal static string Random(int length) {
|
||||||
{
|
|
||||||
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
|
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
|
||||||
var random = new Random();
|
var random = new Random();
|
||||||
return new string(Enumerable.Repeat(chars, length)
|
return new string(Enumerable.Repeat(chars, length)
|
||||||
.Select(s => s[random.Next(s.Length)]).ToArray());
|
.Select(s => s[random.Next(s.Length)]).ToArray());
|
||||||
}
|
}
|
||||||
internal static double CalcProgress(double already, double total)
|
internal static double CalcProgress(double already, double total) {
|
||||||
{
|
|
||||||
var pv = (1.0 * already / total);
|
var pv = (1.0 * already / total);
|
||||||
return Math.Round(pv, 3);
|
return Math.Round(pv, 3);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,28 +6,14 @@ using LeanCloud.Storage.Internal;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal
|
namespace LeanCloud.Storage.Internal {
|
||||||
{
|
internal class AWSS3FileController : AVFileController {
|
||||||
internal class AWSS3FileController : AVFileController
|
public override Task<FileState> SaveAsync(FileState state, Stream dataStream, string sessionToken, IProgress<AVUploadProgressEventArgs> progress, CancellationToken cancellationToken = default(System.Threading.CancellationToken)) {
|
||||||
{
|
if (state.Url != null) {
|
||||||
|
|
||||||
private object mutex = new object();
|
|
||||||
|
|
||||||
|
|
||||||
public AWSS3FileController(IAVCommandRunner commandRunner) : base(commandRunner)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Task<FileState> SaveAsync(FileState state, Stream dataStream, string sessionToken, IProgress<AVUploadProgressEventArgs> progress, CancellationToken cancellationToken = default(System.Threading.CancellationToken))
|
|
||||||
{
|
|
||||||
if (state.Url != null)
|
|
||||||
{
|
|
||||||
return Task.FromResult(state);
|
return Task.FromResult(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetFileToken(state, cancellationToken).OnSuccess(t =>
|
return GetFileToken(state, cancellationToken).OnSuccess(t => {
|
||||||
{
|
|
||||||
var fileToken = t.Result.Item2;
|
var fileToken = t.Result.Item2;
|
||||||
var uploadUrl = fileToken["upload_url"].ToString();
|
var uploadUrl = fileToken["upload_url"].ToString();
|
||||||
state.ObjectId = fileToken["objectId"].ToString();
|
state.ObjectId = fileToken["objectId"].ToString();
|
||||||
|
@ -35,14 +21,12 @@ namespace LeanCloud.Storage.Internal
|
||||||
state.Url = new Uri(url, UriKind.Absolute);
|
state.Url = new Uri(url, UriKind.Absolute);
|
||||||
return PutFile(state, uploadUrl, dataStream);
|
return PutFile(state, uploadUrl, dataStream);
|
||||||
|
|
||||||
}).Unwrap().OnSuccess(s =>
|
}).Unwrap().OnSuccess(s => {
|
||||||
{
|
|
||||||
return s.Result;
|
return s.Result;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<FileState> PutFile(FileState state, string uploadUrl, Stream dataStream)
|
internal async Task<FileState> PutFile(FileState state, string uploadUrl, Stream dataStream) {
|
||||||
{
|
|
||||||
IList<KeyValuePair<string, string>> makeBlockHeaders = new List<KeyValuePair<string, string>>();
|
IList<KeyValuePair<string, string>> makeBlockHeaders = new List<KeyValuePair<string, string>>();
|
||||||
makeBlockHeaders.Add(new KeyValuePair<string, string>("Content-Type", state.MimeType));
|
makeBlockHeaders.Add(new KeyValuePair<string, string>("Content-Type", state.MimeType));
|
||||||
var request = new HttpRequest {
|
var request = new HttpRequest {
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal
|
|
||||||
{
|
|
||||||
public interface IAVFileController
|
|
||||||
{
|
|
||||||
Task<FileState> SaveAsync(FileState state,
|
|
||||||
Stream dataStream,
|
|
||||||
String sessionToken,
|
|
||||||
IProgress<AVUploadProgressEventArgs> progress,
|
|
||||||
CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
Task DeleteAsync(FileState state,
|
|
||||||
string sessionToken,
|
|
||||||
CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
Task<FileState> GetAsync(string objectId,
|
|
||||||
string sessionToken,
|
|
||||||
CancellationToken cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,10 +8,8 @@ using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal
|
namespace LeanCloud.Storage.Internal {
|
||||||
{
|
internal class QCloudCosFileController : AVFileController {
|
||||||
internal class QCloudCosFileController : AVFileController
|
|
||||||
{
|
|
||||||
private object mutex = new object();
|
private object mutex = new object();
|
||||||
|
|
||||||
FileState fileState;
|
FileState fileState;
|
||||||
|
@ -22,32 +20,24 @@ namespace LeanCloud.Storage.Internal
|
||||||
bool done;
|
bool done;
|
||||||
private long sliceSize = (long)CommonSize.KB512;
|
private long sliceSize = (long)CommonSize.KB512;
|
||||||
|
|
||||||
public QCloudCosFileController(IAVCommandRunner commandRunner) : base(commandRunner)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<FileState> SaveAsync(FileState state,
|
public Task<FileState> SaveAsync(FileState state,
|
||||||
Stream dataStream,
|
Stream dataStream,
|
||||||
string sessionToken,
|
string sessionToken,
|
||||||
IProgress<AVUploadProgressEventArgs> progress,
|
IProgress<AVUploadProgressEventArgs> progress,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken) {
|
||||||
{
|
if (state.Url != null) {
|
||||||
if (state.Url != null)
|
|
||||||
{
|
|
||||||
return Task<FileState>.FromResult(state);
|
return Task<FileState>.FromResult(state);
|
||||||
}
|
}
|
||||||
fileState = state;
|
fileState = state;
|
||||||
data = dataStream;
|
data = dataStream;
|
||||||
return GetFileToken(fileState, cancellationToken).OnSuccess(_ =>
|
return GetFileToken(fileState, cancellationToken).OnSuccess(_ => {
|
||||||
{
|
|
||||||
var fileToken = _.Result.Item2;
|
var fileToken = _.Result.Item2;
|
||||||
uploadUrl = fileToken["upload_url"].ToString();
|
uploadUrl = fileToken["upload_url"].ToString();
|
||||||
token = fileToken["token"].ToString();
|
token = fileToken["token"].ToString();
|
||||||
fileState.ObjectId = fileToken["objectId"].ToString();
|
fileState.ObjectId = fileToken["objectId"].ToString();
|
||||||
bucket = fileToken["bucket"].ToString();
|
bucket = fileToken["bucket"].ToString();
|
||||||
|
|
||||||
return FileSlice(cancellationToken).OnSuccess(t =>
|
return FileSlice(cancellationToken).OnSuccess(t => {
|
||||||
{
|
|
||||||
if (done) return Task<FileState>.FromResult(state);
|
if (done) return Task<FileState>.FromResult(state);
|
||||||
var response = t.Result.Item2;
|
var response = t.Result.Item2;
|
||||||
var resumeData = response["data"] as IDictionary<string, object>;
|
var resumeData = response["data"] as IDictionary<string, object>;
|
||||||
|
@ -65,32 +55,25 @@ namespace LeanCloud.Storage.Internal
|
||||||
long offset,
|
long offset,
|
||||||
Stream dataStream,
|
Stream dataStream,
|
||||||
IProgress<AVUploadProgressEventArgs> progress,
|
IProgress<AVUploadProgressEventArgs> progress,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken) {
|
||||||
{
|
|
||||||
|
|
||||||
long dataLength = dataStream.Length;
|
long dataLength = dataStream.Length;
|
||||||
if (progress != null)
|
if (progress != null) {
|
||||||
{
|
lock (mutex) {
|
||||||
lock (mutex)
|
progress.Report(new AVUploadProgressEventArgs() {
|
||||||
{
|
|
||||||
progress.Report(new AVUploadProgressEventArgs()
|
|
||||||
{
|
|
||||||
Progress = AVFileController.CalcProgress(offset, dataLength)
|
Progress = AVFileController.CalcProgress(offset, dataLength)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (offset == dataLength)
|
if (offset == dataLength) {
|
||||||
{
|
|
||||||
return Task.FromResult<FileState>(fileState);
|
return Task.FromResult<FileState>(fileState);
|
||||||
}
|
}
|
||||||
|
|
||||||
var sliceFile = GetNextBinary(offset, dataStream);
|
var sliceFile = GetNextBinary(offset, dataStream);
|
||||||
return ExcuteUpload(sessionId, offset, sliceFile, cancellationToken).OnSuccess(_ =>
|
return ExcuteUpload(sessionId, offset, sliceFile, cancellationToken).OnSuccess(_ => {
|
||||||
{
|
|
||||||
offset += sliceFile.Length;
|
offset += sliceFile.Length;
|
||||||
if (offset == dataLength)
|
if (offset == dataLength) {
|
||||||
{
|
|
||||||
done = true;
|
done = true;
|
||||||
return Task.FromResult<FileState>(fileState);
|
return Task.FromResult<FileState>(fileState);
|
||||||
}
|
}
|
||||||
|
@ -101,8 +84,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
}).Unwrap();
|
}).Unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
Task<Tuple<HttpStatusCode, IDictionary<string, object>>> ExcuteUpload(string sessionId, long offset, byte[] sliceFile, CancellationToken cancellationToken)
|
Task<Tuple<HttpStatusCode, IDictionary<string, object>>> ExcuteUpload(string sessionId, long offset, byte[] sliceFile, CancellationToken cancellationToken) {
|
||||||
{
|
|
||||||
var body = new Dictionary<string, object>();
|
var body = new Dictionary<string, object>();
|
||||||
body.Add("op", "upload_slice");
|
body.Add("op", "upload_slice");
|
||||||
body.Add("session", sessionId);
|
body.Add("session", sessionId);
|
||||||
|
@ -111,26 +93,20 @@ namespace LeanCloud.Storage.Internal
|
||||||
return PostToQCloud(body, sliceFile, cancellationToken);
|
return PostToQCloud(body, sliceFile, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
Task<Tuple<HttpStatusCode, IDictionary<string, object>>> FileSlice(CancellationToken cancellationToken)
|
Task<Tuple<HttpStatusCode, IDictionary<string, object>>> FileSlice(CancellationToken cancellationToken) {
|
||||||
{
|
|
||||||
SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider();
|
SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider();
|
||||||
var body = new Dictionary<string, object>();
|
var body = new Dictionary<string, object>();
|
||||||
if (data.Length <= (long)CommonSize.KB512)
|
if (data.Length <= (long)CommonSize.KB512) {
|
||||||
{
|
|
||||||
body.Add("op", "upload");
|
body.Add("op", "upload");
|
||||||
body.Add("sha", HexStringFromBytes(sha1.ComputeHash(data)));
|
body.Add("sha", HexStringFromBytes(sha1.ComputeHash(data)));
|
||||||
var wholeFile = GetNextBinary(0, data);
|
var wholeFile = GetNextBinary(0, data);
|
||||||
return PostToQCloud(body, wholeFile, cancellationToken).OnSuccess(_ =>
|
return PostToQCloud(body, wholeFile, cancellationToken).OnSuccess(_ => {
|
||||||
{
|
if (_.Result.Item1 == HttpStatusCode.OK) {
|
||||||
if (_.Result.Item1 == HttpStatusCode.OK)
|
|
||||||
{
|
|
||||||
done = true;
|
done = true;
|
||||||
}
|
}
|
||||||
return _.Result;
|
return _.Result;
|
||||||
});
|
});
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
body.Add("op", "upload_slice");
|
body.Add("op", "upload_slice");
|
||||||
body.Add("filesize", data.Length);
|
body.Add("filesize", data.Length);
|
||||||
body.Add("sha", HexStringFromBytes(sha1.ComputeHash(data)));
|
body.Add("sha", HexStringFromBytes(sha1.ComputeHash(data)));
|
||||||
|
@ -139,19 +115,16 @@ namespace LeanCloud.Storage.Internal
|
||||||
|
|
||||||
return PostToQCloud(body, null, cancellationToken);
|
return PostToQCloud(body, null, cancellationToken);
|
||||||
}
|
}
|
||||||
public static string HexStringFromBytes(byte[] bytes)
|
public static string HexStringFromBytes(byte[] bytes) {
|
||||||
{
|
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
foreach (byte b in bytes)
|
foreach (byte b in bytes) {
|
||||||
{
|
|
||||||
var hex = b.ToString("x2");
|
var hex = b.ToString("x2");
|
||||||
sb.Append(hex);
|
sb.Append(hex);
|
||||||
}
|
}
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string SHA1HashStringForUTF8String(string s)
|
public static string SHA1HashStringForUTF8String(string s) {
|
||||||
{
|
|
||||||
byte[] bytes = Encoding.UTF8.GetBytes(s);
|
byte[] bytes = Encoding.UTF8.GetBytes(s);
|
||||||
|
|
||||||
SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider();
|
SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider();
|
||||||
|
@ -162,8 +135,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
async Task<Tuple<HttpStatusCode, IDictionary<string, object>>> PostToQCloud(
|
async Task<Tuple<HttpStatusCode, IDictionary<string, object>>> PostToQCloud(
|
||||||
Dictionary<string, object> body,
|
Dictionary<string, object> body,
|
||||||
byte[] sliceFile,
|
byte[] sliceFile,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken) {
|
||||||
{
|
|
||||||
IList<KeyValuePair<string, string>> sliceHeaders = new List<KeyValuePair<string, string>>();
|
IList<KeyValuePair<string, string>> sliceHeaders = new List<KeyValuePair<string, string>>();
|
||||||
sliceHeaders.Add(new KeyValuePair<string, string>("Authorization", this.token));
|
sliceHeaders.Add(new KeyValuePair<string, string>("Authorization", this.token));
|
||||||
|
|
||||||
|
@ -185,8 +157,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
JsonConvert.DeserializeObject<Dictionary<string, object>>(ret.Item2, new LeanCloudJsonConverter()));
|
JsonConvert.DeserializeObject<Dictionary<string, object>>(ret.Item2, new LeanCloudJsonConverter()));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
public static Stream HttpUploadFile(byte[] file, string fileName, out string contentType, out long contentLength, IDictionary<string, object> nvc)
|
public static Stream HttpUploadFile(byte[] file, string fileName, out string contentType, out long contentLength, IDictionary<string, object> nvc) {
|
||||||
{
|
|
||||||
string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
|
string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
|
||||||
byte[] boundarybytes = StringToAscii("\r\n--" + boundary + "\r\n");
|
byte[] boundarybytes = StringToAscii("\r\n--" + boundary + "\r\n");
|
||||||
contentType = "multipart/form-data; boundary=" + boundary;
|
contentType = "multipart/form-data; boundary=" + boundary;
|
||||||
|
@ -194,8 +165,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
MemoryStream rs = new MemoryStream();
|
MemoryStream rs = new MemoryStream();
|
||||||
|
|
||||||
string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}";
|
string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}";
|
||||||
foreach (string key in nvc.Keys)
|
foreach (string key in nvc.Keys) {
|
||||||
{
|
|
||||||
rs.Write(boundarybytes, 0, boundarybytes.Length);
|
rs.Write(boundarybytes, 0, boundarybytes.Length);
|
||||||
string formitem = string.Format(formdataTemplate, key, nvc[key]);
|
string formitem = string.Format(formdataTemplate, key, nvc[key]);
|
||||||
byte[] formitembytes = System.Text.Encoding.UTF8.GetBytes(formitem);
|
byte[] formitembytes = System.Text.Encoding.UTF8.GetBytes(formitem);
|
||||||
|
@ -203,8 +173,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
}
|
}
|
||||||
rs.Write(boundarybytes, 0, boundarybytes.Length);
|
rs.Write(boundarybytes, 0, boundarybytes.Length);
|
||||||
|
|
||||||
if (file != null)
|
if (file != null) {
|
||||||
{
|
|
||||||
string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n";
|
string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n";
|
||||||
string header = string.Format(headerTemplate, "fileContent", fileName, "application/octet-stream");
|
string header = string.Format(headerTemplate, "fileContent", fileName, "application/octet-stream");
|
||||||
byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes(header);
|
byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes(header);
|
||||||
|
@ -224,11 +193,9 @@ namespace LeanCloud.Storage.Internal
|
||||||
return new MemoryStream(tempBuffer);
|
return new MemoryStream(tempBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] StringToAscii(string s)
|
public static byte[] StringToAscii(string s) {
|
||||||
{
|
|
||||||
byte[] retval = new byte[s.Length];
|
byte[] retval = new byte[s.Length];
|
||||||
for (int ix = 0; ix < s.Length; ++ix)
|
for (int ix = 0; ix < s.Length; ++ix) {
|
||||||
{
|
|
||||||
char ch = s[ix];
|
char ch = s[ix];
|
||||||
if (ch <= 0x7f) retval[ix] = (byte)ch;
|
if (ch <= 0x7f) retval[ix] = (byte)ch;
|
||||||
else retval[ix] = (byte)'?';
|
else retval[ix] = (byte)'?';
|
||||||
|
@ -236,10 +203,8 @@ namespace LeanCloud.Storage.Internal
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] GetNextBinary(long completed, Stream dataStream)
|
byte[] GetNextBinary(long completed, Stream dataStream) {
|
||||||
{
|
if (completed + sliceSize > dataStream.Length) {
|
||||||
if (completed + sliceSize > dataStream.Length)
|
|
||||||
{
|
|
||||||
sliceSize = dataStream.Length - completed;
|
sliceSize = dataStream.Length - completed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,11 +31,6 @@ namespace LeanCloud.Storage.Internal
|
||||||
internal static string UP_HOST = "https://up.qbox.me";
|
internal static string UP_HOST = "https://up.qbox.me";
|
||||||
private object mutex = new object();
|
private object mutex = new object();
|
||||||
|
|
||||||
public QiniuFileController(IAVCommandRunner commandRunner) : base(commandRunner)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Task<FileState> SaveAsync(FileState state,
|
public override Task<FileState> SaveAsync(FileState state,
|
||||||
Stream dataStream,
|
Stream dataStream,
|
||||||
String sessionToken,
|
String sessionToken,
|
||||||
|
@ -165,7 +160,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
Method = HttpMethod.Post,
|
Method = HttpMethod.Post,
|
||||||
Content = parameters
|
Content = parameters
|
||||||
};
|
};
|
||||||
return commandRunner.RunCommandAsync<IDictionary<string, object>>(command);
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command);
|
||||||
}
|
}
|
||||||
IList<KeyValuePair<string, string>> GetQiniuRequestHeaders(FileState state)
|
IList<KeyValuePair<string, string>> GetQiniuRequestHeaders(FileState state)
|
||||||
{
|
{
|
||||||
|
|
|
@ -10,7 +10,7 @@ using System.IO;
|
||||||
using NetHttpClient = System.Net.Http.HttpClient;
|
using NetHttpClient = System.Net.Http.HttpClient;
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal {
|
namespace LeanCloud.Storage.Internal {
|
||||||
public class HttpClient : IHttpClient {
|
public class HttpClient {
|
||||||
static readonly HashSet<string> HttpContentHeaders = new HashSet<string> {
|
static readonly HashSet<string> HttpContentHeaders = new HashSet<string> {
|
||||||
{ "Allow" },
|
{ "Allow" },
|
||||||
{ "Content-Disposition" },
|
{ "Content-Disposition" },
|
||||||
|
@ -41,7 +41,7 @@ namespace LeanCloud.Storage.Internal {
|
||||||
IProgress<AVUploadProgressEventArgs> uploadProgress,
|
IProgress<AVUploadProgressEventArgs> uploadProgress,
|
||||||
IProgress<AVDownloadProgressEventArgs> downloadProgress,
|
IProgress<AVDownloadProgressEventArgs> downloadProgress,
|
||||||
CancellationToken cancellationToken) {
|
CancellationToken cancellationToken) {
|
||||||
|
|
||||||
HttpMethod httpMethod = httpRequest.Method;
|
HttpMethod httpMethod = httpRequest.Method;
|
||||||
HttpRequestMessage message = new HttpRequestMessage(httpMethod, httpRequest.Uri);
|
HttpRequestMessage message = new HttpRequestMessage(httpMethod, httpRequest.Uri);
|
||||||
|
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Net;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal
|
|
||||||
{
|
|
||||||
public interface IHttpClient
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Executes HTTP request to a <see cref="HttpRequest.Uri"/> with <see cref="HttpRequest.Method"/> HTTP verb
|
|
||||||
/// and <see cref="HttpRequest.Headers"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="httpRequest">The HTTP request to be executed.</param>
|
|
||||||
/// <param name="uploadProgress">Upload progress callback.</param>
|
|
||||||
/// <param name="downloadProgress">Download progress callback.</param>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
/// <returns>A task that resolves to Htt</returns>
|
|
||||||
Task<Tuple<HttpStatusCode, string>> ExecuteAsync(HttpRequest httpRequest,
|
|
||||||
IProgress<AVUploadProgressEventArgs> uploadProgress,
|
|
||||||
IProgress<AVDownloadProgressEventArgs> downloadProgress,
|
|
||||||
CancellationToken cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
using LeanCloud.Storage.Internal;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal
|
|
||||||
{
|
|
||||||
public interface IAVCorePlugins
|
|
||||||
{
|
|
||||||
void Reset();
|
|
||||||
|
|
||||||
IHttpClient HttpClient { get; }
|
|
||||||
IAppRouterController AppRouterController { get; }
|
|
||||||
IAVCommandRunner CommandRunner { get; }
|
|
||||||
IStorageController StorageController { get; }
|
|
||||||
|
|
||||||
IAVCloudCodeController CloudCodeController { get; }
|
|
||||||
IAVConfigController ConfigController { get; }
|
|
||||||
IAVFileController FileController { get; }
|
|
||||||
IAVObjectController ObjectController { get; }
|
|
||||||
IAVQueryController QueryController { get; }
|
|
||||||
IAVSessionController SessionController { get; }
|
|
||||||
IAVUserController UserController { get; }
|
|
||||||
IObjectSubclassingController SubclassingController { get; }
|
|
||||||
IAVCurrentUserController CurrentUserController { get; }
|
|
||||||
IInstallationIdController InstallationIdController { get; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal {
|
|
||||||
public interface IInstallationIdController {
|
|
||||||
/// <summary>
|
|
||||||
/// Sets current <code>installationId</code> and saves it to local storage.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="installationId">The <code>installationId</code> to be saved.</param>
|
|
||||||
Task SetAsync(Guid? installationId);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets current <code>installationId</code> from local storage. Generates a none exists.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Current <code>installationId</code>.</returns>
|
|
||||||
Task<Guid?> GetAsync();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Clears current installationId from memory and local storage.
|
|
||||||
/// </summary>
|
|
||||||
Task ClearAsync();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,66 +1,59 @@
|
||||||
using LeanCloud.Storage.Internal;
|
using System;
|
||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal {
|
namespace LeanCloud.Storage.Internal {
|
||||||
public class InstallationIdController : IInstallationIdController {
|
public class InstallationIdController {
|
||||||
private const string InstallationIdKey = "InstallationId";
|
private const string InstallationIdKey = "InstallationId";
|
||||||
private readonly object mutex = new object();
|
private readonly object mutex = new object();
|
||||||
private Guid? installationId;
|
private Guid? installationId;
|
||||||
|
|
||||||
private readonly IStorageController storageController;
|
public Task SetAsync(Guid? installationId) {
|
||||||
public InstallationIdController(IStorageController storageController) {
|
|
||||||
this.storageController = storageController;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task SetAsync(Guid? installationId) {
|
|
||||||
lock (mutex) {
|
|
||||||
Task saveTask;
|
|
||||||
|
|
||||||
if (installationId == null) {
|
|
||||||
saveTask = storageController
|
|
||||||
.LoadAsync()
|
|
||||||
.OnSuccess(storage => storage.Result.RemoveAsync(InstallationIdKey))
|
|
||||||
.Unwrap();
|
|
||||||
} else {
|
|
||||||
saveTask = storageController
|
|
||||||
.LoadAsync()
|
|
||||||
.OnSuccess(storage => storage.Result.AddAsync(InstallationIdKey, installationId.ToString()))
|
|
||||||
.Unwrap();
|
|
||||||
}
|
|
||||||
this.installationId = installationId;
|
|
||||||
return saveTask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<Guid?> GetAsync() {
|
|
||||||
lock (mutex) {
|
|
||||||
if (installationId != null) {
|
|
||||||
return Task.FromResult(installationId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return storageController
|
|
||||||
.LoadAsync()
|
|
||||||
.OnSuccess<IStorageDictionary<string, object>, Task<Guid?>>(s => {
|
|
||||||
object id;
|
|
||||||
s.Result.TryGetValue(InstallationIdKey, out id);
|
|
||||||
try {
|
|
||||||
lock (mutex) {
|
lock (mutex) {
|
||||||
installationId = new Guid((string)id);
|
Task saveTask;
|
||||||
return Task.FromResult(installationId);
|
|
||||||
}
|
|
||||||
} catch (Exception) {
|
|
||||||
var newInstallationId = Guid.NewGuid();
|
|
||||||
return SetAsync(newInstallationId).OnSuccess<Guid?>(_ => newInstallationId);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.Unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task ClearAsync() {
|
if (installationId == null) {
|
||||||
return SetAsync(null);
|
saveTask = AVPlugins.Instance.StorageController
|
||||||
|
.LoadAsync()
|
||||||
|
.OnSuccess(storage => storage.Result.RemoveAsync(InstallationIdKey))
|
||||||
|
.Unwrap();
|
||||||
|
} else {
|
||||||
|
saveTask = AVPlugins.Instance.StorageController
|
||||||
|
.LoadAsync()
|
||||||
|
.OnSuccess(storage => storage.Result.AddAsync(InstallationIdKey, installationId.ToString()))
|
||||||
|
.Unwrap();
|
||||||
|
}
|
||||||
|
this.installationId = installationId;
|
||||||
|
return saveTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<Guid?> GetAsync() {
|
||||||
|
lock (mutex) {
|
||||||
|
if (installationId != null) {
|
||||||
|
return Task.FromResult(installationId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return AVPlugins.Instance.StorageController
|
||||||
|
.LoadAsync()
|
||||||
|
.OnSuccess(s => {
|
||||||
|
object id;
|
||||||
|
s.Result.TryGetValue(InstallationIdKey, out id);
|
||||||
|
try {
|
||||||
|
lock (mutex) {
|
||||||
|
installationId = new Guid((string)id);
|
||||||
|
return Task.FromResult(installationId);
|
||||||
|
}
|
||||||
|
} catch (Exception) {
|
||||||
|
var newInstallationId = Guid.NewGuid();
|
||||||
|
return SetAsync(newInstallationId).OnSuccess<Guid?>(_ => newInstallationId);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.Unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task ClearAsync() {
|
||||||
|
return SetAsync(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,13 +7,7 @@ using LeanCloud.Utilities;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal {
|
namespace LeanCloud.Storage.Internal {
|
||||||
public class AVObjectController : IAVObjectController {
|
public class AVObjectController {
|
||||||
private readonly IAVCommandRunner commandRunner;
|
|
||||||
|
|
||||||
public AVObjectController(IAVCommandRunner commandRunner) {
|
|
||||||
this.commandRunner = commandRunner;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<IObjectState> FetchAsync(IObjectState state,
|
public Task<IObjectState> FetchAsync(IObjectState state,
|
||||||
string sessionToken,
|
string sessionToken,
|
||||||
CancellationToken cancellationToken) {
|
CancellationToken cancellationToken) {
|
||||||
|
@ -21,7 +15,7 @@ namespace LeanCloud.Storage.Internal {
|
||||||
Path = $"classes/{Uri.EscapeDataString(state.ClassName)}/{Uri.EscapeDataString(state.ObjectId)}",
|
Path = $"classes/{Uri.EscapeDataString(state.ClassName)}/{Uri.EscapeDataString(state.ObjectId)}",
|
||||||
Method = HttpMethod.Get
|
Method = HttpMethod.Get
|
||||||
};
|
};
|
||||||
return commandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t => {
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t => {
|
||||||
return AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
|
return AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -34,7 +28,7 @@ namespace LeanCloud.Storage.Internal {
|
||||||
Path = $"classes/{Uri.EscapeDataString(state.ClassName)}/{Uri.EscapeDataString(state.ObjectId)}?{AVClient.BuildQueryString(queryString)}",
|
Path = $"classes/{Uri.EscapeDataString(state.ClassName)}/{Uri.EscapeDataString(state.ObjectId)}?{AVClient.BuildQueryString(queryString)}",
|
||||||
Method = HttpMethod.Get
|
Method = HttpMethod.Get
|
||||||
};
|
};
|
||||||
return commandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t => {
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t => {
|
||||||
return AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
|
return AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -49,7 +43,7 @@ namespace LeanCloud.Storage.Internal {
|
||||||
Method = state.ObjectId == null ? HttpMethod.Post : HttpMethod.Put,
|
Method = state.ObjectId == null ? HttpMethod.Post : HttpMethod.Put,
|
||||||
Content = objectJSON
|
Content = objectJSON
|
||||||
};
|
};
|
||||||
return commandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t => {
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t => {
|
||||||
var serverState = AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
|
var serverState = AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
|
||||||
serverState = serverState.MutatedClone(mutableClone => {
|
serverState = serverState.MutatedClone(mutableClone => {
|
||||||
mutableClone.IsNew = t.Result.Item1 == System.Net.HttpStatusCode.Created;
|
mutableClone.IsNew = t.Result.Item1 == System.Net.HttpStatusCode.Created;
|
||||||
|
@ -62,7 +56,7 @@ namespace LeanCloud.Storage.Internal {
|
||||||
IList<IDictionary<string, IAVFieldOperation>> operationsList,
|
IList<IDictionary<string, IAVFieldOperation>> operationsList,
|
||||||
string sessionToken,
|
string sessionToken,
|
||||||
CancellationToken cancellationToken) {
|
CancellationToken cancellationToken) {
|
||||||
|
|
||||||
var requests = states
|
var requests = states
|
||||||
.Zip(operationsList, (item, ops) => new AVCommand {
|
.Zip(operationsList, (item, ops) => new AVCommand {
|
||||||
Path = item.ObjectId == null ? $"classes/{Uri.EscapeDataString(item.ClassName)}" : $"classes/{Uri.EscapeDataString(item.ClassName)}/{Uri.EscapeDataString(item.ObjectId)}",
|
Path = item.ObjectId == null ? $"classes/{Uri.EscapeDataString(item.ClassName)}" : $"classes/{Uri.EscapeDataString(item.ClassName)}/{Uri.EscapeDataString(item.ObjectId)}",
|
||||||
|
@ -89,7 +83,7 @@ namespace LeanCloud.Storage.Internal {
|
||||||
Path = $"classes/{state.ClassName}/{state.ObjectId}",
|
Path = $"classes/{state.ClassName}/{state.ObjectId}",
|
||||||
Method = HttpMethod.Delete
|
Method = HttpMethod.Delete
|
||||||
};
|
};
|
||||||
return commandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken);
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IList<Task> DeleteAllAsync(IList<IObjectState> states,
|
public IList<Task> DeleteAllAsync(IList<IObjectState> states,
|
||||||
|
@ -157,7 +151,7 @@ namespace LeanCloud.Storage.Internal {
|
||||||
{ "requests", encodedRequests }
|
{ "requests", encodedRequests }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
commandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).ContinueWith(t => {
|
AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).ContinueWith(t => {
|
||||||
if (t.IsFaulted || t.IsCanceled) {
|
if (t.IsFaulted || t.IsCanceled) {
|
||||||
foreach (var tcs in tcss) {
|
foreach (var tcs in tcss) {
|
||||||
if (t.IsFaulted) {
|
if (t.IsFaulted) {
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal
|
|
||||||
{
|
|
||||||
public interface IAVObjectController
|
|
||||||
{
|
|
||||||
//Task<IObjectState> FetchAsync(IObjectState state,
|
|
||||||
// string sessionToken,
|
|
||||||
// CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
Task<IObjectState> FetchAsync(IObjectState state,
|
|
||||||
IDictionary<string,object> queryString,
|
|
||||||
string sessionToken,
|
|
||||||
CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
Task<IObjectState> SaveAsync(IObjectState state,
|
|
||||||
IDictionary<string, IAVFieldOperation> operations,
|
|
||||||
string sessionToken,
|
|
||||||
CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
IList<Task<IObjectState>> SaveAllAsync(IList<IObjectState> states,
|
|
||||||
IList<IDictionary<string, IAVFieldOperation>> operationsList,
|
|
||||||
string sessionToken,
|
|
||||||
CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
Task DeleteAsync(IObjectState state,
|
|
||||||
string sessionToken,
|
|
||||||
CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
IList<Task> DeleteAllAsync(IList<IObjectState> states,
|
|
||||||
string sessionToken,
|
|
||||||
CancellationToken cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal
|
|
||||||
{
|
|
||||||
public interface IObjectSubclassingController
|
|
||||||
{
|
|
||||||
String GetClassName(Type type);
|
|
||||||
Type GetType(String className);
|
|
||||||
|
|
||||||
bool IsTypeValid(String className, Type type);
|
|
||||||
|
|
||||||
void RegisterSubclass(Type t);
|
|
||||||
void UnregisterSubclass(Type t);
|
|
||||||
|
|
||||||
void AddRegisterHook(Type t, Action action);
|
|
||||||
|
|
||||||
AVObject Instantiate(String className);
|
|
||||||
IDictionary<String, String> GetPropertyMappings(String className);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +1,19 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using LeanCloud.Storage.Internal;
|
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal
|
namespace LeanCloud.Storage.Internal {
|
||||||
{
|
public class ObjectSubclassingController {
|
||||||
internal class ObjectSubclassingController : IObjectSubclassingController
|
|
||||||
{
|
|
||||||
// Class names starting with _ are documented to be reserved. Use this one
|
// Class names starting with _ are documented to be reserved. Use this one
|
||||||
// here to allow us to 'inherit' certain properties.
|
// here to allow us to 'inherit' certain properties.
|
||||||
private static readonly string avObjectClassName = "_AVObject";
|
private static readonly string avObjectClassName = "_AVObject";
|
||||||
|
|
||||||
private readonly ReaderWriterLockSlim mutex;
|
private readonly ReaderWriterLockSlim mutex;
|
||||||
private readonly IDictionary<String, ObjectSubclassInfo> registeredSubclasses;
|
private readonly IDictionary<string, ObjectSubclassInfo> registeredSubclasses;
|
||||||
private Dictionary<String, Action> registerActions;
|
private Dictionary<string, Action> registerActions;
|
||||||
|
|
||||||
public ObjectSubclassingController()
|
public ObjectSubclassingController() {
|
||||||
{
|
|
||||||
mutex = new ReaderWriterLockSlim();
|
mutex = new ReaderWriterLockSlim();
|
||||||
registeredSubclasses = new Dictionary<String, ObjectSubclassInfo>();
|
registeredSubclasses = new Dictionary<String, ObjectSubclassInfo>();
|
||||||
registerActions = new Dictionary<string, Action>();
|
registerActions = new Dictionary<string, Action>();
|
||||||
|
@ -28,15 +23,13 @@ namespace LeanCloud.Storage.Internal
|
||||||
RegisterSubclass(typeof(AVObject));
|
RegisterSubclass(typeof(AVObject));
|
||||||
}
|
}
|
||||||
|
|
||||||
public String GetClassName(Type type)
|
public string GetClassName(Type type) {
|
||||||
{
|
|
||||||
return type == typeof(AVObject)
|
return type == typeof(AVObject)
|
||||||
? avObjectClassName
|
? avObjectClassName
|
||||||
: ObjectSubclassInfo.GetClassName(type.GetTypeInfo());
|
: ObjectSubclassInfo.GetClassName(type.GetTypeInfo());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Type GetType(String className)
|
public Type GetType(string className) {
|
||||||
{
|
|
||||||
ObjectSubclassInfo info = null;
|
ObjectSubclassInfo info = null;
|
||||||
mutex.EnterReadLock();
|
mutex.EnterReadLock();
|
||||||
registeredSubclasses.TryGetValue(className, out info);
|
registeredSubclasses.TryGetValue(className, out info);
|
||||||
|
@ -47,8 +40,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsTypeValid(String className, Type type)
|
public bool IsTypeValid(string className, Type type) {
|
||||||
{
|
|
||||||
ObjectSubclassInfo subclassInfo = null;
|
ObjectSubclassInfo subclassInfo = null;
|
||||||
|
|
||||||
mutex.EnterReadLock();
|
mutex.EnterReadLock();
|
||||||
|
@ -60,39 +52,30 @@ namespace LeanCloud.Storage.Internal
|
||||||
: subclassInfo.TypeInfo == type.GetTypeInfo();
|
: subclassInfo.TypeInfo == type.GetTypeInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RegisterSubclass(Type type)
|
public void RegisterSubclass(Type type) {
|
||||||
{
|
|
||||||
TypeInfo typeInfo = type.GetTypeInfo();
|
TypeInfo typeInfo = type.GetTypeInfo();
|
||||||
if (!typeof(AVObject).GetTypeInfo().IsAssignableFrom(typeInfo))
|
if (!typeof(AVObject).GetTypeInfo().IsAssignableFrom(typeInfo)) {
|
||||||
{
|
|
||||||
throw new ArgumentException("Cannot register a type that is not a subclass of AVObject");
|
throw new ArgumentException("Cannot register a type that is not a subclass of AVObject");
|
||||||
}
|
}
|
||||||
|
|
||||||
String className = GetClassName(type);
|
string className = GetClassName(type);
|
||||||
|
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
// Perform this as a single independent transaction, so we can never get into an
|
// Perform this as a single independent transaction, so we can never get into an
|
||||||
// intermediate state where we *theoretically* register the wrong class due to a
|
// intermediate state where we *theoretically* register the wrong class due to a
|
||||||
// TOCTTOU bug.
|
// TOCTTOU bug.
|
||||||
mutex.EnterWriteLock();
|
mutex.EnterWriteLock();
|
||||||
|
|
||||||
ObjectSubclassInfo previousInfo = null;
|
ObjectSubclassInfo previousInfo = null;
|
||||||
if (registeredSubclasses.TryGetValue(className, out previousInfo))
|
if (registeredSubclasses.TryGetValue(className, out previousInfo)) {
|
||||||
{
|
if (typeInfo.IsAssignableFrom(previousInfo.TypeInfo)) {
|
||||||
if (typeInfo.IsAssignableFrom(previousInfo.TypeInfo))
|
|
||||||
{
|
|
||||||
// Previous subclass is more specific or equal to the current type, do nothing.
|
// Previous subclass is more specific or equal to the current type, do nothing.
|
||||||
return;
|
return;
|
||||||
}
|
} else if (previousInfo.TypeInfo.IsAssignableFrom(typeInfo)) {
|
||||||
else if (previousInfo.TypeInfo.IsAssignableFrom(typeInfo))
|
|
||||||
{
|
|
||||||
// Previous subclass is parent of new child, fallthrough and actually register
|
// Previous subclass is parent of new child, fallthrough and actually register
|
||||||
// this class.
|
// this class.
|
||||||
/* Do nothing */
|
/* Do nothing */
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new ArgumentException(
|
throw new ArgumentException(
|
||||||
"Tried to register both " + previousInfo.TypeInfo.FullName + " and " + typeInfo.FullName +
|
"Tried to register both " + previousInfo.TypeInfo.FullName + " and " + typeInfo.FullName +
|
||||||
" as the AVObject subclass of " + className + ". Cannot determine the right class " +
|
" as the AVObject subclass of " + className + ". Cannot determine the right class " +
|
||||||
|
@ -102,15 +85,12 @@ namespace LeanCloud.Storage.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
ConstructorInfo constructor = type.FindConstructor();
|
ConstructorInfo constructor = type.FindConstructor();
|
||||||
if (constructor == null)
|
if (constructor == null) {
|
||||||
{
|
|
||||||
throw new ArgumentException("Cannot register a type that does not implement the default constructor!");
|
throw new ArgumentException("Cannot register a type that does not implement the default constructor!");
|
||||||
}
|
}
|
||||||
|
|
||||||
registeredSubclasses[className] = new ObjectSubclassInfo(type, constructor);
|
registeredSubclasses[className] = new ObjectSubclassInfo(type, constructor);
|
||||||
}
|
} finally {
|
||||||
finally
|
|
||||||
{
|
|
||||||
mutex.ExitWriteLock();
|
mutex.ExitWriteLock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,28 +100,24 @@ namespace LeanCloud.Storage.Internal
|
||||||
registerActions.TryGetValue(className, out toPerform);
|
registerActions.TryGetValue(className, out toPerform);
|
||||||
mutex.ExitReadLock();
|
mutex.ExitReadLock();
|
||||||
|
|
||||||
if (toPerform != null)
|
if (toPerform != null) {
|
||||||
{
|
|
||||||
toPerform();
|
toPerform();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UnregisterSubclass(Type type)
|
public void UnregisterSubclass(Type type) {
|
||||||
{
|
|
||||||
mutex.EnterWriteLock();
|
mutex.EnterWriteLock();
|
||||||
registeredSubclasses.Remove(GetClassName(type));
|
registeredSubclasses.Remove(GetClassName(type));
|
||||||
mutex.ExitWriteLock();
|
mutex.ExitWriteLock();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddRegisterHook(Type t, Action action)
|
public void AddRegisterHook(Type t, Action action) {
|
||||||
{
|
|
||||||
mutex.EnterWriteLock();
|
mutex.EnterWriteLock();
|
||||||
registerActions.Add(GetClassName(t), action);
|
registerActions.Add(GetClassName(t), action);
|
||||||
mutex.ExitWriteLock();
|
mutex.ExitWriteLock();
|
||||||
}
|
}
|
||||||
|
|
||||||
public AVObject Instantiate(String className)
|
public AVObject Instantiate(String className) {
|
||||||
{
|
|
||||||
ObjectSubclassInfo info = null;
|
ObjectSubclassInfo info = null;
|
||||||
|
|
||||||
mutex.EnterReadLock();
|
mutex.EnterReadLock();
|
||||||
|
@ -153,13 +129,11 @@ namespace LeanCloud.Storage.Internal
|
||||||
: new AVObject(className);
|
: new AVObject(className);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IDictionary<String, String> GetPropertyMappings(String className)
|
public IDictionary<String, String> GetPropertyMappings(String className) {
|
||||||
{
|
|
||||||
ObjectSubclassInfo info = null;
|
ObjectSubclassInfo info = null;
|
||||||
mutex.EnterReadLock();
|
mutex.EnterReadLock();
|
||||||
registeredSubclasses.TryGetValue(className, out info);
|
registeredSubclasses.TryGetValue(className, out info);
|
||||||
if (info == null)
|
if (info == null) {
|
||||||
{
|
|
||||||
registeredSubclasses.TryGetValue(avObjectClassName, out info);
|
registeredSubclasses.TryGetValue(avObjectClassName, out info);
|
||||||
}
|
}
|
||||||
mutex.ExitReadLock();
|
mutex.ExitReadLock();
|
||||||
|
|
|
@ -5,25 +5,13 @@ using System.Net.Http;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal
|
namespace LeanCloud.Storage.Internal {
|
||||||
{
|
public class AVQueryController {
|
||||||
internal class AVQueryController : IAVQueryController
|
public Task<IEnumerable<IObjectState>> FindAsync<T>(AVQuery<T> query, AVUser user,
|
||||||
{
|
CancellationToken cancellationToken) where T : AVObject {
|
||||||
private readonly IAVCommandRunner commandRunner;
|
|
||||||
|
|
||||||
public AVQueryController(IAVCommandRunner commandRunner)
|
|
||||||
{
|
|
||||||
this.commandRunner = commandRunner;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<IEnumerable<IObjectState>> FindAsync<T>(AVQuery<T> query,
|
|
||||||
AVUser user,
|
|
||||||
CancellationToken cancellationToken) where T : AVObject
|
|
||||||
{
|
|
||||||
string sessionToken = user != null ? user.SessionToken : null;
|
string sessionToken = user != null ? user.SessionToken : null;
|
||||||
|
|
||||||
return FindAsync(query.Path, query.BuildParameters(), sessionToken, cancellationToken).OnSuccess(t =>
|
return FindAsync(query.Path, query.BuildParameters(), sessionToken, cancellationToken).OnSuccess(t => {
|
||||||
{
|
|
||||||
var items = t.Result["results"] as IList<object>;
|
var items = t.Result["results"] as IList<object>;
|
||||||
|
|
||||||
return (from item in items
|
return (from item in items
|
||||||
|
@ -33,35 +21,30 @@ namespace LeanCloud.Storage.Internal
|
||||||
|
|
||||||
public Task<int> CountAsync<T>(AVQuery<T> query,
|
public Task<int> CountAsync<T>(AVQuery<T> query,
|
||||||
AVUser user,
|
AVUser user,
|
||||||
CancellationToken cancellationToken) where T : AVObject
|
CancellationToken cancellationToken) where T : AVObject {
|
||||||
{
|
|
||||||
string sessionToken = user != null ? user.SessionToken : null;
|
string sessionToken = user != null ? user.SessionToken : null;
|
||||||
var parameters = query.BuildParameters();
|
var parameters = query.BuildParameters();
|
||||||
parameters["limit"] = 0;
|
parameters["limit"] = 0;
|
||||||
parameters["count"] = 1;
|
parameters["count"] = 1;
|
||||||
|
|
||||||
return FindAsync(query.Path, parameters, sessionToken, cancellationToken).OnSuccess(t =>
|
return FindAsync(query.Path, parameters, sessionToken, cancellationToken).OnSuccess(t => {
|
||||||
{
|
|
||||||
return Convert.ToInt32(t.Result["count"]);
|
return Convert.ToInt32(t.Result["count"]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IObjectState> FirstAsync<T>(AVQuery<T> query,
|
public Task<IObjectState> FirstAsync<T>(AVQuery<T> query,
|
||||||
AVUser user,
|
AVUser user,
|
||||||
CancellationToken cancellationToken) where T : AVObject
|
CancellationToken cancellationToken) where T : AVObject {
|
||||||
{
|
|
||||||
string sessionToken = user?.SessionToken;
|
string sessionToken = user?.SessionToken;
|
||||||
var parameters = query.BuildParameters();
|
var parameters = query.BuildParameters();
|
||||||
parameters["limit"] = 1;
|
parameters["limit"] = 1;
|
||||||
|
|
||||||
return FindAsync(query.Path, parameters, sessionToken, cancellationToken).OnSuccess(t =>
|
return FindAsync(query.Path, parameters, sessionToken, cancellationToken).OnSuccess(t => {
|
||||||
{
|
|
||||||
var items = t.Result["results"] as IList<object>;
|
var items = t.Result["results"] as IList<object>;
|
||||||
var item = items.FirstOrDefault() as IDictionary<string, object>;
|
var item = items.FirstOrDefault() as IDictionary<string, object>;
|
||||||
|
|
||||||
// Not found. Return empty state.
|
// Not found. Return empty state.
|
||||||
if (item == null)
|
if (item == null) {
|
||||||
{
|
|
||||||
return (IObjectState)null;
|
return (IObjectState)null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,34 +55,14 @@ namespace LeanCloud.Storage.Internal
|
||||||
private Task<IDictionary<string, object>> FindAsync(string path,
|
private Task<IDictionary<string, object>> FindAsync(string path,
|
||||||
IDictionary<string, object> parameters,
|
IDictionary<string, object> parameters,
|
||||||
string sessionToken,
|
string sessionToken,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken) {
|
||||||
{
|
|
||||||
var command = new AVCommand {
|
var command = new AVCommand {
|
||||||
Path = $"{path}?{AVClient.BuildQueryString(parameters)}",
|
Path = $"{path}?{AVClient.BuildQueryString(parameters)}",
|
||||||
Method = HttpMethod.Get
|
Method = HttpMethod.Get
|
||||||
};
|
};
|
||||||
return commandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t =>
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t => {
|
||||||
{
|
|
||||||
return t.Result.Item2;
|
return t.Result.Item2;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//private Task<IDictionary<string, object>> FindAsync(string className,
|
|
||||||
// IDictionary<string, object> parameters,
|
|
||||||
// string sessionToken,
|
|
||||||
// CancellationToken cancellationToken)
|
|
||||||
//{
|
|
||||||
// var command = new AVCommand(string.Format("classes/{0}?{1}",
|
|
||||||
// Uri.EscapeDataString(className),
|
|
||||||
// AVClient.BuildQueryString(parameters)),
|
|
||||||
// method: "GET",
|
|
||||||
// sessionToken: sessionToken,
|
|
||||||
// data: null);
|
|
||||||
|
|
||||||
// return commandRunner.RunCommandAsync(command, cancellationToken: cancellationToken).OnSuccess(t =>
|
|
||||||
// {
|
|
||||||
// return t.Result.Item2;
|
|
||||||
// });
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal {
|
|
||||||
public interface IAVQueryController {
|
|
||||||
Task<IEnumerable<IObjectState>> FindAsync<T>(AVQuery<T> query,
|
|
||||||
AVUser user,
|
|
||||||
CancellationToken cancellationToken) where T : AVObject;
|
|
||||||
|
|
||||||
Task<int> CountAsync<T>(AVQuery<T> query,
|
|
||||||
AVUser user,
|
|
||||||
CancellationToken cancellationToken) where T : AVObject;
|
|
||||||
|
|
||||||
Task<IObjectState> FirstAsync<T>(AVQuery<T> query,
|
|
||||||
AVUser user,
|
|
||||||
CancellationToken cancellationToken) where T : AVObject;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,19 +5,13 @@ using System.Threading.Tasks;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal {
|
namespace LeanCloud.Storage.Internal {
|
||||||
public class AVSessionController : IAVSessionController {
|
public class AVSessionController {
|
||||||
private readonly IAVCommandRunner commandRunner;
|
|
||||||
|
|
||||||
public AVSessionController(IAVCommandRunner commandRunner) {
|
|
||||||
this.commandRunner = commandRunner;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<IObjectState> GetSessionAsync(string sessionToken, CancellationToken cancellationToken) {
|
public Task<IObjectState> GetSessionAsync(string sessionToken, CancellationToken cancellationToken) {
|
||||||
var command = new AVCommand {
|
var command = new AVCommand {
|
||||||
Path = "sessions/me",
|
Path = "sessions/me",
|
||||||
Method = HttpMethod.Get
|
Method = HttpMethod.Get
|
||||||
};
|
};
|
||||||
return commandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t => {
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t => {
|
||||||
return AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
|
return AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -27,7 +21,7 @@ namespace LeanCloud.Storage.Internal {
|
||||||
Path = "logout",
|
Path = "logout",
|
||||||
Method = HttpMethod.Post
|
Method = HttpMethod.Post
|
||||||
};
|
};
|
||||||
return commandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken);
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IObjectState> UpgradeToRevocableSessionAsync(string sessionToken, CancellationToken cancellationToken) {
|
public Task<IObjectState> UpgradeToRevocableSessionAsync(string sessionToken, CancellationToken cancellationToken) {
|
||||||
|
@ -35,7 +29,7 @@ namespace LeanCloud.Storage.Internal {
|
||||||
Path = "upgradeToRevocableSession",
|
Path = "upgradeToRevocableSession",
|
||||||
Method = HttpMethod.Post,
|
Method = HttpMethod.Post,
|
||||||
};
|
};
|
||||||
return commandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t => {
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t => {
|
||||||
return AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
|
return AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal {
|
|
||||||
public interface IAVSessionController {
|
|
||||||
Task<IObjectState> GetSessionAsync(string sessionToken, CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
Task RevokeAsync(string sessionToken, CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
Task<IObjectState> UpgradeToRevocableSessionAsync(string sessionToken, CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
bool IsRevocableSessionToken(string sessionToken);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal {
|
|
||||||
/// <summary>
|
|
||||||
/// An abstraction for accessing persistent storage in the LeanCloud SDK.
|
|
||||||
/// </summary>
|
|
||||||
public interface IStorageController {
|
|
||||||
/// <summary>
|
|
||||||
/// Load the contents of this storage controller asynchronously.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<IStorageDictionary<string, object>> LoadAsync();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Overwrites the contents of this storage controller asynchronously.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="contents"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task<IStorageDictionary<string, object>> SaveAsync(IDictionary<string, object> contents);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// An interface for a dictionary that is persisted to disk asynchronously.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TKey">They key type of the dictionary.</typeparam>
|
|
||||||
/// <typeparam name="TValue">The value type of the dictionary.</typeparam>
|
|
||||||
public interface IStorageDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>> {
|
|
||||||
int Count { get; }
|
|
||||||
TValue this[TKey key] { get; }
|
|
||||||
|
|
||||||
IEnumerable<TKey> Keys { get; }
|
|
||||||
IEnumerable<TValue> Values { get; }
|
|
||||||
|
|
||||||
bool ContainsKey(TKey key);
|
|
||||||
bool TryGetValue(TKey key, out TValue value);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a key to this dictionary, and saves it asynchronously.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key">The key to insert.</param>
|
|
||||||
/// <param name="value">The value to insert.</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task AddAsync(TKey key, TValue value);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes a key from this dictionary, and saves it asynchronously.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
Task RemoveAsync(TKey key);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,28 +6,23 @@ using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal
|
namespace LeanCloud.Storage.Internal {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Implements `IStorageController` for PCL targets, based off of PCLStorage.
|
/// Implements `IStorageController` for PCL targets, based off of PCLStorage.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class StorageController : IStorageController
|
public class StorageController {
|
||||||
{
|
public class StorageDictionary : IEnumerable<KeyValuePair<string, object>> {
|
||||||
private class StorageDictionary : IStorageDictionary<string, object>
|
|
||||||
{
|
|
||||||
private readonly string filePath;
|
private readonly string filePath;
|
||||||
|
|
||||||
private Dictionary<string, object> dictionary;
|
private Dictionary<string, object> dictionary;
|
||||||
readonly ReaderWriterLockSlim locker = new ReaderWriterLockSlim();
|
readonly ReaderWriterLockSlim locker = new ReaderWriterLockSlim();
|
||||||
|
|
||||||
public StorageDictionary(string filePath)
|
public StorageDictionary(string filePath) {
|
||||||
{
|
|
||||||
this.filePath = filePath;
|
this.filePath = filePath;
|
||||||
dictionary = new Dictionary<string, object>();
|
dictionary = new Dictionary<string, object>();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Task SaveAsync()
|
internal Task SaveAsync() {
|
||||||
{
|
|
||||||
string json;
|
string json;
|
||||||
locker.EnterReadLock();
|
locker.EnterReadLock();
|
||||||
json = JsonConvert.SerializeObject(dictionary);
|
json = JsonConvert.SerializeObject(dictionary);
|
||||||
|
@ -37,8 +32,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task LoadAsync()
|
internal async Task LoadAsync() {
|
||||||
{
|
|
||||||
using (var sr = new StreamReader(filePath)) {
|
using (var sr = new StreamReader(filePath)) {
|
||||||
var text = await sr.ReadToEndAsync();
|
var text = await sr.ReadToEndAsync();
|
||||||
Dictionary<string, object> result = null;
|
Dictionary<string, object> result = null;
|
||||||
|
@ -54,31 +48,27 @@ namespace LeanCloud.Storage.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Update(IDictionary<string, object> contents)
|
internal void Update(IDictionary<string, object> contents) {
|
||||||
{
|
|
||||||
locker.EnterWriteLock();
|
locker.EnterWriteLock();
|
||||||
dictionary = contents.ToDictionary(p => p.Key, p => p.Value);
|
dictionary = contents.ToDictionary(p => p.Key, p => p.Value);
|
||||||
locker.ExitWriteLock();
|
locker.ExitWriteLock();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task AddAsync(string key, object value)
|
public Task AddAsync(string key, object value) {
|
||||||
{
|
|
||||||
locker.EnterWriteLock();
|
locker.EnterWriteLock();
|
||||||
dictionary[key] = value;
|
dictionary[key] = value;
|
||||||
locker.ExitWriteLock();
|
locker.ExitWriteLock();
|
||||||
return SaveAsync();
|
return SaveAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task RemoveAsync(string key)
|
public Task RemoveAsync(string key) {
|
||||||
{
|
|
||||||
locker.EnterWriteLock();
|
locker.EnterWriteLock();
|
||||||
dictionary.Remove(key);
|
dictionary.Remove(key);
|
||||||
locker.ExitWriteLock();
|
locker.ExitWriteLock();
|
||||||
return SaveAsync();
|
return SaveAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ContainsKey(string key)
|
public bool ContainsKey(string key) {
|
||||||
{
|
|
||||||
try {
|
try {
|
||||||
locker.EnterReadLock();
|
locker.EnterReadLock();
|
||||||
return dictionary.ContainsKey(key);
|
return dictionary.ContainsKey(key);
|
||||||
|
@ -87,8 +77,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<string> Keys
|
public IEnumerable<string> Keys {
|
||||||
{
|
|
||||||
get {
|
get {
|
||||||
try {
|
try {
|
||||||
locker.EnterReadLock();
|
locker.EnterReadLock();
|
||||||
|
@ -99,8 +88,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetValue(string key, out object value)
|
public bool TryGetValue(string key, out object value) {
|
||||||
{
|
|
||||||
try {
|
try {
|
||||||
locker.EnterReadLock();
|
locker.EnterReadLock();
|
||||||
return dictionary.TryGetValue(key, out value);
|
return dictionary.TryGetValue(key, out value);
|
||||||
|
@ -109,8 +97,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<object> Values
|
public IEnumerable<object> Values {
|
||||||
{
|
|
||||||
get {
|
get {
|
||||||
try {
|
try {
|
||||||
locker.EnterReadLock();
|
locker.EnterReadLock();
|
||||||
|
@ -121,8 +108,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public object this[string key]
|
public object this[string key] {
|
||||||
{
|
|
||||||
get {
|
get {
|
||||||
try {
|
try {
|
||||||
locker.EnterReadLock();
|
locker.EnterReadLock();
|
||||||
|
@ -133,8 +119,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Count
|
public int Count {
|
||||||
{
|
|
||||||
get {
|
get {
|
||||||
try {
|
try {
|
||||||
locker.EnterReadLock();
|
locker.EnterReadLock();
|
||||||
|
@ -145,8 +130,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
|
public IEnumerator<KeyValuePair<string, object>> GetEnumerator() {
|
||||||
{
|
|
||||||
try {
|
try {
|
||||||
locker.EnterReadLock();
|
locker.EnterReadLock();
|
||||||
return dictionary.GetEnumerator();
|
return dictionary.GetEnumerator();
|
||||||
|
@ -155,8 +139,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
|
||||||
{
|
|
||||||
try {
|
try {
|
||||||
locker.EnterReadLock();
|
locker.EnterReadLock();
|
||||||
return dictionary.GetEnumerator();
|
return dictionary.GetEnumerator();
|
||||||
|
@ -171,46 +154,38 @@ namespace LeanCloud.Storage.Internal
|
||||||
private readonly Task<string> fileTask;
|
private readonly Task<string> fileTask;
|
||||||
private StorageDictionary storageDictionary;
|
private StorageDictionary storageDictionary;
|
||||||
|
|
||||||
public StorageController(string fileNamePrefix)
|
public StorageController(string fileNamePrefix) {
|
||||||
{
|
fileTask = taskQueue.Enqueue(t => t.ContinueWith(_ => {
|
||||||
fileTask = taskQueue.Enqueue(t => t.ContinueWith(_ =>
|
|
||||||
{
|
|
||||||
string path = $"{fileNamePrefix}_{LeanCloudStorageFileName}";
|
string path = $"{fileNamePrefix}_{LeanCloudStorageFileName}";
|
||||||
File.CreateText(path);
|
File.CreateText(path);
|
||||||
return path;
|
return path;
|
||||||
}), CancellationToken.None);
|
}), CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IStorageDictionary<string, object>> LoadAsync()
|
public Task<StorageDictionary> LoadAsync() {
|
||||||
{
|
return taskQueue.Enqueue(toAwait => {
|
||||||
return taskQueue.Enqueue(toAwait =>
|
return toAwait.ContinueWith(_ => {
|
||||||
{
|
if (storageDictionary != null) {
|
||||||
return toAwait.ContinueWith(_ =>
|
return Task.FromResult(storageDictionary);
|
||||||
{
|
|
||||||
if (storageDictionary != null)
|
|
||||||
{
|
|
||||||
return Task.FromResult<IStorageDictionary<string, object>>(storageDictionary);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
storageDictionary = new StorageDictionary(fileTask.Result);
|
storageDictionary = new StorageDictionary(fileTask.Result);
|
||||||
return storageDictionary.LoadAsync().OnSuccess(__ => storageDictionary as IStorageDictionary<string, object>);
|
return storageDictionary.LoadAsync()
|
||||||
|
.OnSuccess(__ => storageDictionary);
|
||||||
}).Unwrap();
|
}).Unwrap();
|
||||||
}, CancellationToken.None);
|
}, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IStorageDictionary<string, object>> SaveAsync(IDictionary<string, object> contents)
|
public Task<StorageDictionary> SaveAsync(IDictionary<string, object> contents) {
|
||||||
{
|
return taskQueue.Enqueue(toAwait => {
|
||||||
return taskQueue.Enqueue(toAwait =>
|
return toAwait.ContinueWith(_ => {
|
||||||
{
|
if (storageDictionary == null) {
|
||||||
return toAwait.ContinueWith(_ =>
|
|
||||||
{
|
|
||||||
if (storageDictionary == null)
|
|
||||||
{
|
|
||||||
storageDictionary = new StorageDictionary(fileTask.Result);
|
storageDictionary = new StorageDictionary(fileTask.Result);
|
||||||
}
|
}
|
||||||
|
|
||||||
storageDictionary.Update(contents);
|
storageDictionary.Update(contents);
|
||||||
return storageDictionary.SaveAsync().OnSuccess(__ => storageDictionary as IStorageDictionary<string, object>);
|
return storageDictionary.SaveAsync()
|
||||||
|
.OnSuccess(__ => storageDictionary);
|
||||||
}).Unwrap();
|
}).Unwrap();
|
||||||
}, CancellationToken.None);
|
}, CancellationToken.None);
|
||||||
}
|
}
|
|
@ -9,17 +9,9 @@ using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal
|
namespace LeanCloud.Storage.Internal
|
||||||
{
|
{
|
||||||
public class AVCurrentUserController : IAVCurrentUserController
|
public class AVCurrentUserController
|
||||||
{
|
{
|
||||||
private readonly object mutex = new object();
|
private readonly object mutex = new object();
|
||||||
private readonly TaskQueue taskQueue = new TaskQueue();
|
|
||||||
|
|
||||||
private IStorageController storageController;
|
|
||||||
|
|
||||||
public AVCurrentUserController(IStorageController storageController)
|
|
||||||
{
|
|
||||||
this.storageController = storageController;
|
|
||||||
}
|
|
||||||
|
|
||||||
private AVUser currentUser;
|
private AVUser currentUser;
|
||||||
public AVUser CurrentUser
|
public AVUser CurrentUser
|
||||||
|
@ -45,7 +37,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
Task saveTask = null;
|
Task saveTask = null;
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
saveTask = storageController
|
saveTask = AVPlugins.Instance.StorageController
|
||||||
.LoadAsync()
|
.LoadAsync()
|
||||||
.OnSuccess(t => t.Result.RemoveAsync("CurrentUser"))
|
.OnSuccess(t => t.Result.RemoveAsync("CurrentUser"))
|
||||||
.Unwrap();
|
.Unwrap();
|
||||||
|
@ -65,7 +57,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
CultureInfo.InvariantCulture);
|
CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
|
|
||||||
saveTask = storageController
|
saveTask = AVPlugins.Instance.StorageController
|
||||||
.LoadAsync()
|
.LoadAsync()
|
||||||
.OnSuccess(t => t.Result.AddAsync("CurrentUser", JsonConvert.SerializeObject(data)))
|
.OnSuccess(t => t.Result.AddAsync("CurrentUser", JsonConvert.SerializeObject(data)))
|
||||||
.Unwrap();
|
.Unwrap();
|
||||||
|
@ -89,7 +81,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
return Task<AVUser>.FromResult(cachedCurrent);
|
return Task<AVUser>.FromResult(cachedCurrent);
|
||||||
}
|
}
|
||||||
|
|
||||||
return storageController.LoadAsync().OnSuccess(t =>
|
return AVPlugins.Instance.StorageController.LoadAsync().OnSuccess(t =>
|
||||||
{
|
{
|
||||||
object temp;
|
object temp;
|
||||||
t.Result.TryGetValue("CurrentUser", out temp);
|
t.Result.TryGetValue("CurrentUser", out temp);
|
||||||
|
@ -114,7 +106,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
return Task<bool>.FromResult(true);
|
return Task<bool>.FromResult(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return storageController.LoadAsync().OnSuccess(t => t.Result.ContainsKey("CurrentUser"));
|
return AVPlugins.Instance.StorageController.LoadAsync().OnSuccess(t => t.Result.ContainsKey("CurrentUser"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsCurrent(AVUser user)
|
public bool IsCurrent(AVUser user)
|
||||||
|
@ -136,7 +128,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
{
|
{
|
||||||
ClearFromMemory();
|
ClearFromMemory();
|
||||||
|
|
||||||
storageController.LoadAsync().OnSuccess(t => t.Result.RemoveAsync("CurrentUser"));
|
AVPlugins.Instance.StorageController.LoadAsync().OnSuccess(t => t.Result.RemoveAsync("CurrentUser"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,15 +6,8 @@ using System.Net.Http;
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal
|
namespace LeanCloud.Storage.Internal
|
||||||
{
|
{
|
||||||
public class AVUserController : IAVUserController
|
public class AVUserController
|
||||||
{
|
{
|
||||||
private readonly IAVCommandRunner commandRunner;
|
|
||||||
|
|
||||||
public AVUserController(IAVCommandRunner commandRunner)
|
|
||||||
{
|
|
||||||
this.commandRunner = commandRunner;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<IObjectState> SignUpAsync(IObjectState state,
|
public Task<IObjectState> SignUpAsync(IObjectState state,
|
||||||
IDictionary<string, IAVFieldOperation> operations,
|
IDictionary<string, IAVFieldOperation> operations,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
|
@ -25,7 +18,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
Method = HttpMethod.Post,
|
Method = HttpMethod.Post,
|
||||||
Content = objectJSON
|
Content = objectJSON
|
||||||
};
|
};
|
||||||
return commandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t =>
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t =>
|
||||||
{
|
{
|
||||||
var serverState = AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
|
var serverState = AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
|
||||||
serverState = serverState.MutatedClone(mutableClone =>
|
serverState = serverState.MutatedClone(mutableClone =>
|
||||||
|
@ -54,7 +47,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
Method = HttpMethod.Post,
|
Method = HttpMethod.Post,
|
||||||
Content = data
|
Content = data
|
||||||
};
|
};
|
||||||
return commandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t =>
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t =>
|
||||||
{
|
{
|
||||||
var serverState = AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
|
var serverState = AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
|
||||||
serverState = serverState.MutatedClone(mutableClone =>
|
serverState = serverState.MutatedClone(mutableClone =>
|
||||||
|
@ -80,7 +73,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
{ "authData", authData}
|
{ "authData", authData}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return commandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t =>
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t =>
|
||||||
{
|
{
|
||||||
var serverState = AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
|
var serverState = AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
|
||||||
serverState = serverState.MutatedClone(mutableClone =>
|
serverState = serverState.MutatedClone(mutableClone =>
|
||||||
|
@ -97,7 +90,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
Path = "users/me",
|
Path = "users/me",
|
||||||
Method = HttpMethod.Get
|
Method = HttpMethod.Get
|
||||||
};
|
};
|
||||||
return commandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t =>
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t =>
|
||||||
{
|
{
|
||||||
return AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
|
return AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
|
||||||
});
|
});
|
||||||
|
@ -112,7 +105,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
{ "email", email}
|
{ "email", email}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return commandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken);
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IObjectState> LogInWithParametersAsync(string relativeUrl, IDictionary<string, object> data,
|
public Task<IObjectState> LogInWithParametersAsync(string relativeUrl, IDictionary<string, object> data,
|
||||||
|
@ -123,7 +116,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
Method = HttpMethod.Post,
|
Method = HttpMethod.Post,
|
||||||
Content = data
|
Content = data
|
||||||
};
|
};
|
||||||
return commandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t =>
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t =>
|
||||||
{
|
{
|
||||||
var serverState = AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
|
var serverState = AVObjectCoder.Instance.Decode(t.Result.Item2, AVDecoder.Instance);
|
||||||
serverState = serverState.MutatedClone(mutableClone =>
|
serverState = serverState.MutatedClone(mutableClone =>
|
||||||
|
@ -144,7 +137,7 @@ namespace LeanCloud.Storage.Internal
|
||||||
{ "new_password", newPassword },
|
{ "new_password", newPassword },
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return commandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken);
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IObjectState> RefreshSessionTokenAsync(string userId, string sessionToken,
|
public Task<IObjectState> RefreshSessionTokenAsync(string userId, string sessionToken,
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal {
|
|
||||||
public interface IAVCurrentUserController : IAVObjectCurrentController<AVUser> {
|
|
||||||
Task<string> GetCurrentSessionTokenAsync(CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
Task LogOutAsync(CancellationToken cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal
|
|
||||||
{
|
|
||||||
public interface IAVUserController
|
|
||||||
{
|
|
||||||
Task<IObjectState> SignUpAsync(IObjectState state,
|
|
||||||
IDictionary<string, IAVFieldOperation> operations,
|
|
||||||
CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
Task<IObjectState> LogInAsync(string username,
|
|
||||||
string email,
|
|
||||||
string password,
|
|
||||||
CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
Task<IObjectState> LogInWithParametersAsync(string relativeUrl,
|
|
||||||
IDictionary<string, object> data,
|
|
||||||
CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
Task<IObjectState> LogInAsync(string authType,
|
|
||||||
IDictionary<string, object> data,
|
|
||||||
bool failOnNotExist,
|
|
||||||
CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
Task<IObjectState> GetUserAsync(string sessionToken,
|
|
||||||
CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
Task RequestPasswordResetAsync(string email,
|
|
||||||
CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
Task UpdatePasswordAsync(string usedId, string sessionToken,
|
|
||||||
string oldPassword, string newPassword,
|
|
||||||
CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
Task<IObjectState> RefreshSessionTokenAsync(string userId,
|
|
||||||
string sessionToken,
|
|
||||||
CancellationToken cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace LeanCloud.Storage.Internal {
|
|
||||||
/// <summary>
|
|
||||||
/// So here's the deal. We have a lot of internal APIs for AVObject, AVUser, etc.
|
|
||||||
///
|
|
||||||
/// These cannot be 'internal' anymore if we are fully modularizing things out, because
|
|
||||||
/// they are no longer a part of the same library, especially as we create things like
|
|
||||||
/// Installation inside push library.
|
|
||||||
///
|
|
||||||
/// So this class contains a bunch of extension methods that can live inside another
|
|
||||||
/// namespace, which 'wrap' the intenral APIs that already exist.
|
|
||||||
/// </summary>
|
|
||||||
public static class AVConfigExtensions {
|
|
||||||
public static AVConfig Create(IDictionary<string, object> fetchedConfig) {
|
|
||||||
return new AVConfig(fetchedConfig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -21,12 +21,9 @@ namespace LeanCloud {
|
||||||
/// await AVCloud.CallFunctionAsync<IDictionary<string, object>>("validateGame", parameters);
|
/// await AVCloud.CallFunctionAsync<IDictionary<string, object>>("validateGame", parameters);
|
||||||
/// </code>
|
/// </code>
|
||||||
/// </example>
|
/// </example>
|
||||||
public static class AVCloud
|
public static class AVCloud {
|
||||||
{
|
internal static AVCloudCodeController CloudCodeController {
|
||||||
internal static IAVCloudCodeController CloudCodeController
|
get {
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return AVPlugins.Instance.CloudCodeController;
|
return AVPlugins.Instance.CloudCodeController;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,12 +41,10 @@ namespace LeanCloud {
|
||||||
/// <param name="sesstionToken"></param>
|
/// <param name="sesstionToken"></param>
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>The result of the cloud call.</returns>
|
/// <returns>The result of the cloud call.</returns>
|
||||||
public static Task<T> CallFunctionAsync<T>(String name, IDictionary<string, object> parameters = null, string sesstionToken = null, CancellationToken cancellationToken = default(CancellationToken))
|
public static Task<T> CallFunctionAsync<T>(String name, IDictionary<string, object> parameters = null, string sesstionToken = null, CancellationToken cancellationToken = default(CancellationToken)) {
|
||||||
{
|
|
||||||
var sessionTokenTask = AVUser.TakeSessionToken(sesstionToken);
|
var sessionTokenTask = AVUser.TakeSessionToken(sesstionToken);
|
||||||
|
|
||||||
return sessionTokenTask.OnSuccess(s =>
|
return sessionTokenTask.OnSuccess(s => {
|
||||||
{
|
|
||||||
return CloudCodeController.CallFunctionAsync<T>(name,
|
return CloudCodeController.CallFunctionAsync<T>(name,
|
||||||
parameters, s.Result,
|
parameters, s.Result,
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
|
@ -66,12 +61,10 @@ namespace LeanCloud {
|
||||||
/// <param name="sesstionToken"></param>
|
/// <param name="sesstionToken"></param>
|
||||||
/// <param name="cancellationToken"></param>
|
/// <param name="cancellationToken"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static Task<T> RPCFunctionAsync<T>(String name, IDictionary<string, object> parameters = null, string sesstionToken = null, CancellationToken cancellationToken = default(CancellationToken))
|
public static Task<T> RPCFunctionAsync<T>(String name, IDictionary<string, object> parameters = null, string sesstionToken = null, CancellationToken cancellationToken = default(CancellationToken)) {
|
||||||
{
|
|
||||||
var sessionTokenTask = AVUser.TakeSessionToken(sesstionToken);
|
var sessionTokenTask = AVUser.TakeSessionToken(sesstionToken);
|
||||||
|
|
||||||
return sessionTokenTask.OnSuccess(s =>
|
return sessionTokenTask.OnSuccess(s => {
|
||||||
{
|
|
||||||
return CloudCodeController.RPCFunction<T>(name,
|
return CloudCodeController.RPCFunction<T>(name,
|
||||||
parameters,
|
parameters,
|
||||||
s.Result,
|
s.Result,
|
||||||
|
@ -87,8 +80,7 @@ namespace LeanCloud {
|
||||||
/// <param name="op">进行的操作名称。</param>
|
/// <param name="op">进行的操作名称。</param>
|
||||||
/// <param name="ttl">验证码失效时间。</param>
|
/// <param name="ttl">验证码失效时间。</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static Task RequestSMSCodeAsync(string mobilePhoneNumber, string name, string op, int ttl = 10)
|
public static Task RequestSMSCodeAsync(string mobilePhoneNumber, string name, string op, int ttl = 10) {
|
||||||
{
|
|
||||||
return RequestSMSCodeAsync(mobilePhoneNumber, name, op, ttl, CancellationToken.None);
|
return RequestSMSCodeAsync(mobilePhoneNumber, name, op, ttl, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,10 +94,8 @@ namespace LeanCloud {
|
||||||
/// <param name="op">进行的操作名称。</param>
|
/// <param name="op">进行的操作名称。</param>
|
||||||
/// <param name="ttl">验证码失效时间。</param>
|
/// <param name="ttl">验证码失效时间。</param>
|
||||||
/// <param name="cancellationToken">Cancellation token。</param>
|
/// <param name="cancellationToken">Cancellation token。</param>
|
||||||
public static Task RequestSMSCodeAsync(string mobilePhoneNumber, string name, string op, int ttl = 10, CancellationToken cancellationToken = default(CancellationToken))
|
public static Task RequestSMSCodeAsync(string mobilePhoneNumber, string name, string op, int ttl = 10, CancellationToken cancellationToken = default(CancellationToken)) {
|
||||||
{
|
if (string.IsNullOrEmpty(mobilePhoneNumber)) {
|
||||||
if (string.IsNullOrEmpty(mobilePhoneNumber))
|
|
||||||
{
|
|
||||||
throw new AVException(AVException.ErrorCode.MobilePhoneInvalid, "Moblie Phone number is invalid.", null);
|
throw new AVException(AVException.ErrorCode.MobilePhoneInvalid, "Moblie Phone number is invalid.", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,16 +103,13 @@ namespace LeanCloud {
|
||||||
{
|
{
|
||||||
{ "mobilePhoneNumber", mobilePhoneNumber },
|
{ "mobilePhoneNumber", mobilePhoneNumber },
|
||||||
};
|
};
|
||||||
if (!string.IsNullOrEmpty(name))
|
if (!string.IsNullOrEmpty(name)) {
|
||||||
{
|
|
||||||
strs.Add("name", name);
|
strs.Add("name", name);
|
||||||
}
|
}
|
||||||
if (!string.IsNullOrEmpty(op))
|
if (!string.IsNullOrEmpty(op)) {
|
||||||
{
|
|
||||||
strs.Add("op", op);
|
strs.Add("op", op);
|
||||||
}
|
}
|
||||||
if (ttl > 0)
|
if (ttl > 0) {
|
||||||
{
|
|
||||||
strs.Add("TTL", ttl);
|
strs.Add("TTL", ttl);
|
||||||
}
|
}
|
||||||
var command = new EngineCommand {
|
var command = new EngineCommand {
|
||||||
|
@ -138,8 +125,7 @@ namespace LeanCloud {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>是否发送成功。</returns>
|
/// <returns>是否发送成功。</returns>
|
||||||
/// <param name="mobilePhoneNumber">手机号。</param>
|
/// <param name="mobilePhoneNumber">手机号。</param>
|
||||||
public static Task RequestSMSCodeAsync(string mobilePhoneNumber)
|
public static Task RequestSMSCodeAsync(string mobilePhoneNumber) {
|
||||||
{
|
|
||||||
return RequestSMSCodeAsync(mobilePhoneNumber, CancellationToken.None);
|
return RequestSMSCodeAsync(mobilePhoneNumber, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,8 +136,7 @@ namespace LeanCloud {
|
||||||
/// <returns>是否发送成功。</returns>
|
/// <returns>是否发送成功。</returns>
|
||||||
/// <param name="mobilePhoneNumber">手机号。</param>
|
/// <param name="mobilePhoneNumber">手机号。</param>
|
||||||
/// <param name="cancellationToken">Cancellation Token.</param>
|
/// <param name="cancellationToken">Cancellation Token.</param>
|
||||||
public static Task RequestSMSCodeAsync(string mobilePhoneNumber, CancellationToken cancellationToken)
|
public static Task RequestSMSCodeAsync(string mobilePhoneNumber, CancellationToken cancellationToken) {
|
||||||
{
|
|
||||||
return RequestSMSCodeAsync(mobilePhoneNumber, null, null, 0, cancellationToken);
|
return RequestSMSCodeAsync(mobilePhoneNumber, null, null, 0, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,11 +157,9 @@ namespace LeanCloud {
|
||||||
IDictionary<string, object> env,
|
IDictionary<string, object> env,
|
||||||
string sign = "",
|
string sign = "",
|
||||||
string validateToken = "",
|
string validateToken = "",
|
||||||
CancellationToken cancellationToken = default(CancellationToken))
|
CancellationToken cancellationToken = default(CancellationToken)) {
|
||||||
{
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(mobilePhoneNumber))
|
if (string.IsNullOrEmpty(mobilePhoneNumber)) {
|
||||||
{
|
|
||||||
throw new AVException(AVException.ErrorCode.MobilePhoneInvalid, "Moblie Phone number is invalid.", null);
|
throw new AVException(AVException.ErrorCode.MobilePhoneInvalid, "Moblie Phone number is invalid.", null);
|
||||||
}
|
}
|
||||||
Dictionary<string, object> strs = new Dictionary<string, object>()
|
Dictionary<string, object> strs = new Dictionary<string, object>()
|
||||||
|
@ -184,16 +167,13 @@ namespace LeanCloud {
|
||||||
{ "mobilePhoneNumber", mobilePhoneNumber },
|
{ "mobilePhoneNumber", mobilePhoneNumber },
|
||||||
};
|
};
|
||||||
strs.Add("template", template);
|
strs.Add("template", template);
|
||||||
if (String.IsNullOrEmpty(sign))
|
if (String.IsNullOrEmpty(sign)) {
|
||||||
{
|
|
||||||
strs.Add("sign", sign);
|
strs.Add("sign", sign);
|
||||||
}
|
}
|
||||||
if (String.IsNullOrEmpty(validateToken))
|
if (String.IsNullOrEmpty(validateToken)) {
|
||||||
{
|
|
||||||
strs.Add("validate_token", validateToken);
|
strs.Add("validate_token", validateToken);
|
||||||
}
|
}
|
||||||
foreach (var key in env.Keys)
|
foreach (var key in env.Keys) {
|
||||||
{
|
|
||||||
strs.Add(key, env[key]);
|
strs.Add(key, env[key]);
|
||||||
}
|
}
|
||||||
var command = new EngineCommand {
|
var command = new EngineCommand {
|
||||||
|
@ -209,10 +189,8 @@ namespace LeanCloud {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="mobilePhoneNumber"></param>
|
/// <param name="mobilePhoneNumber"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static Task RequestVoiceCodeAsync(string mobilePhoneNumber)
|
public static Task RequestVoiceCodeAsync(string mobilePhoneNumber) {
|
||||||
{
|
if (string.IsNullOrEmpty(mobilePhoneNumber)) {
|
||||||
if (string.IsNullOrEmpty(mobilePhoneNumber))
|
|
||||||
{
|
|
||||||
throw new AVException(AVException.ErrorCode.MobilePhoneInvalid, "Moblie Phone number is invalid.", null);
|
throw new AVException(AVException.ErrorCode.MobilePhoneInvalid, "Moblie Phone number is invalid.", null);
|
||||||
}
|
}
|
||||||
Dictionary<string, object> body = new Dictionary<string, object>()
|
Dictionary<string, object> body = new Dictionary<string, object>()
|
||||||
|
@ -237,8 +215,7 @@ namespace LeanCloud {
|
||||||
/// <returns>是否验证通过。</returns>
|
/// <returns>是否验证通过。</returns>
|
||||||
/// <param name="mobilePhoneNumber">手机号</param>
|
/// <param name="mobilePhoneNumber">手机号</param>
|
||||||
/// <param name="code">验证码。</param>
|
/// <param name="code">验证码。</param>
|
||||||
public static Task VerifySmsCodeAsync(string code, string mobilePhoneNumber)
|
public static Task VerifySmsCodeAsync(string code, string mobilePhoneNumber) {
|
||||||
{
|
|
||||||
return VerifySmsCodeAsync(code, mobilePhoneNumber, CancellationToken.None);
|
return VerifySmsCodeAsync(code, mobilePhoneNumber, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,8 +226,7 @@ namespace LeanCloud {
|
||||||
/// <param name="code">验证码。</param>
|
/// <param name="code">验证码。</param>
|
||||||
/// <param name="mobilePhoneNumber">手机号</param>
|
/// <param name="mobilePhoneNumber">手机号</param>
|
||||||
/// <param name="cancellationToken">Cancellation token.</param>
|
/// <param name="cancellationToken">Cancellation token.</param>
|
||||||
public static Task VerifySmsCodeAsync(string code, string mobilePhoneNumber, CancellationToken cancellationToken)
|
public static Task VerifySmsCodeAsync(string code, string mobilePhoneNumber, CancellationToken cancellationToken) {
|
||||||
{
|
|
||||||
var command = new AVCommand {
|
var command = new AVCommand {
|
||||||
Path = $"verifySmsCode/{code.Trim()}?mobilePhoneNumber={mobilePhoneNumber.Trim()}",
|
Path = $"verifySmsCode/{code.Trim()}?mobilePhoneNumber={mobilePhoneNumber.Trim()}",
|
||||||
};
|
};
|
||||||
|
@ -260,8 +236,7 @@ namespace LeanCloud {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Stands for a captcha result.
|
/// Stands for a captcha result.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Captcha
|
public class Captcha {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used for captcha verify.
|
/// Used for captcha verify.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -278,8 +253,7 @@ namespace LeanCloud {
|
||||||
/// <param name="code">User's input of this captcha.</param>
|
/// <param name="code">User's input of this captcha.</param>
|
||||||
/// <param name="cancellationToken">CancellationToken.</param>
|
/// <param name="cancellationToken">CancellationToken.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public Task VerifyAsync(string code, CancellationToken cancellationToken = default(CancellationToken))
|
public Task VerifyAsync(string code, CancellationToken cancellationToken = default(CancellationToken)) {
|
||||||
{
|
|
||||||
return AVCloud.VerifyCaptchaAsync(code, Token);
|
return AVCloud.VerifyCaptchaAsync(code, Token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -291,18 +265,15 @@ namespace LeanCloud {
|
||||||
/// <param name="height">captcha image height.</param>
|
/// <param name="height">captcha image height.</param>
|
||||||
/// <param name="cancellationToken">CancellationToken.</param>
|
/// <param name="cancellationToken">CancellationToken.</param>
|
||||||
/// <returns>an instance of Captcha.</returns>
|
/// <returns>an instance of Captcha.</returns>
|
||||||
public static Task<Captcha> RequestCaptchaAsync(int width = 85, int height = 30, CancellationToken cancellationToken = default(CancellationToken))
|
public static Task<Captcha> RequestCaptchaAsync(int width = 85, int height = 30, CancellationToken cancellationToken = default(CancellationToken)) {
|
||||||
{
|
|
||||||
var path = String.Format("requestCaptcha?width={0}&height={1}", width, height);
|
var path = String.Format("requestCaptcha?width={0}&height={1}", width, height);
|
||||||
var command = new AVCommand {
|
var command = new AVCommand {
|
||||||
Path = $"requestCaptcha?width={width}&height={height}",
|
Path = $"requestCaptcha?width={width}&height={height}",
|
||||||
Method = HttpMethod.Get
|
Method = HttpMethod.Get
|
||||||
};
|
};
|
||||||
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t =>
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t => {
|
||||||
{
|
|
||||||
var decoded = AVDecoder.Instance.Decode(t.Result.Item2) as IDictionary<string, object>;
|
var decoded = AVDecoder.Instance.Decode(t.Result.Item2) as IDictionary<string, object>;
|
||||||
return new Captcha()
|
return new Captcha() {
|
||||||
{
|
|
||||||
Token = decoded["captcha_token"] as string,
|
Token = decoded["captcha_token"] as string,
|
||||||
Url = decoded["captcha_url"] as string,
|
Url = decoded["captcha_url"] as string,
|
||||||
};
|
};
|
||||||
|
@ -316,8 +287,7 @@ namespace LeanCloud {
|
||||||
/// <param name="code">User's input of this captcha.</param>
|
/// <param name="code">User's input of this captcha.</param>
|
||||||
/// <param name="cancellationToken">CancellationToken.</param>
|
/// <param name="cancellationToken">CancellationToken.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static Task<string> VerifyCaptchaAsync(string code, string token, CancellationToken cancellationToken = default(CancellationToken))
|
public static Task<string> VerifyCaptchaAsync(string code, string token, CancellationToken cancellationToken = default(CancellationToken)) {
|
||||||
{
|
|
||||||
var data = new Dictionary<string, object> {
|
var data = new Dictionary<string, object> {
|
||||||
{ "captcha_token", token },
|
{ "captcha_token", token },
|
||||||
{ "captcha_code", code },
|
{ "captcha_code", code },
|
||||||
|
@ -327,8 +297,7 @@ namespace LeanCloud {
|
||||||
Method = HttpMethod.Post,
|
Method = HttpMethod.Post,
|
||||||
Content = data
|
Content = data
|
||||||
};
|
};
|
||||||
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).ContinueWith(t =>
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).ContinueWith(t => {
|
||||||
{
|
|
||||||
if (!t.Result.Item2.ContainsKey("validate_token"))
|
if (!t.Result.Item2.ContainsKey("validate_token"))
|
||||||
throw new KeyNotFoundException("validate_token");
|
throw new KeyNotFoundException("validate_token");
|
||||||
return t.Result.Item2["validate_token"] as string;
|
return t.Result.Item2["validate_token"] as string;
|
||||||
|
@ -340,38 +309,32 @@ namespace LeanCloud {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="cancellationToken"></param>
|
/// <param name="cancellationToken"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static Task<IDictionary<string, object>> GetCustomParametersAsync(CancellationToken cancellationToken = default(CancellationToken))
|
public static Task<IDictionary<string, object>> GetCustomParametersAsync(CancellationToken cancellationToken = default(CancellationToken)) {
|
||||||
{
|
|
||||||
var command = new AVCommand {
|
var command = new AVCommand {
|
||||||
Path = $"statistics/apps/{AVClient.CurrentConfiguration.ApplicationId}/sendPolicy",
|
Path = $"statistics/apps/{AVClient.CurrentConfiguration.ApplicationId}/sendPolicy",
|
||||||
Method = HttpMethod.Get
|
Method = HttpMethod.Get
|
||||||
};
|
};
|
||||||
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t =>
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).OnSuccess(t => {
|
||||||
{
|
|
||||||
var settings = t.Result.Item2;
|
var settings = t.Result.Item2;
|
||||||
var CloudParameters = settings["parameters"] as IDictionary<string, object>;
|
var CloudParameters = settings["parameters"] as IDictionary<string, object>;
|
||||||
return CloudParameters;
|
return CloudParameters;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RealtimeSignature
|
public class RealtimeSignature {
|
||||||
{
|
|
||||||
public string Nonce { internal set; get; }
|
public string Nonce { internal set; get; }
|
||||||
public long Timestamp { internal set; get; }
|
public long Timestamp { internal set; get; }
|
||||||
public string ClientId { internal set; get; }
|
public string ClientId { internal set; get; }
|
||||||
public string Signature { internal set; get; }
|
public string Signature { internal set; get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task<RealtimeSignature> RequestRealtimeSignatureAsync(CancellationToken cancellationToken = default(CancellationToken))
|
public static Task<RealtimeSignature> RequestRealtimeSignatureAsync(CancellationToken cancellationToken = default(CancellationToken)) {
|
||||||
{
|
return AVUser.GetCurrentUserAsync(cancellationToken).OnSuccess(t => {
|
||||||
return AVUser.GetCurrentUserAsync(cancellationToken).OnSuccess(t =>
|
|
||||||
{
|
|
||||||
return RequestRealtimeSignatureAsync(t.Result, cancellationToken);
|
return RequestRealtimeSignatureAsync(t.Result, cancellationToken);
|
||||||
}).Unwrap();
|
}).Unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task<RealtimeSignature> RequestRealtimeSignatureAsync(AVUser user, CancellationToken cancellationToken = default(CancellationToken))
|
public static Task<RealtimeSignature> RequestRealtimeSignatureAsync(AVUser user, CancellationToken cancellationToken = default(CancellationToken)) {
|
||||||
{
|
|
||||||
var command = new AVCommand {
|
var command = new AVCommand {
|
||||||
Path = "rtm/sign",
|
Path = "rtm/sign",
|
||||||
Method = HttpMethod.Post,
|
Method = HttpMethod.Post,
|
||||||
|
@ -379,11 +342,9 @@ namespace LeanCloud {
|
||||||
{ "session_token", user.SessionToken }
|
{ "session_token", user.SessionToken }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).ContinueWith(t =>
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command, cancellationToken: cancellationToken).ContinueWith(t => {
|
||||||
{
|
|
||||||
var body = t.Result.Item2;
|
var body = t.Result.Item2;
|
||||||
return new RealtimeSignature()
|
return new RealtimeSignature() {
|
||||||
{
|
|
||||||
Nonce = body["nonce"] as string,
|
Nonce = body["nonce"] as string,
|
||||||
Timestamp = (long)body["timestamp"],
|
Timestamp = (long)body["timestamp"],
|
||||||
ClientId = body["client_id"] as string,
|
ClientId = body["client_id"] as string,
|
||||||
|
@ -396,8 +357,7 @@ namespace LeanCloud {
|
||||||
/// Gets the LeanEngine hosting URL for current app async.
|
/// Gets the LeanEngine hosting URL for current app async.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The lean engine hosting URL async.</returns>
|
/// <returns>The lean engine hosting URL async.</returns>
|
||||||
public static Task<string> GetLeanEngineHostingUrlAsync()
|
public static Task<string> GetLeanEngineHostingUrlAsync() {
|
||||||
{
|
|
||||||
return CallFunctionAsync<string>("_internal_extensions_get_domain");
|
return CallFunctionAsync<string>("_internal_extensions_get_domain");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -407,8 +367,7 @@ namespace LeanCloud {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// AVRPCC loud function base.
|
/// AVRPCC loud function base.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AVRPCCloudFunctionBase<P, R>
|
public class AVRPCCloudFunctionBase<P, R> {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// AVRPCD eserialize.
|
/// AVRPCD eserialize.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -419,17 +378,13 @@ namespace LeanCloud {
|
||||||
public delegate IDictionary<string, object> AVRPCSerialize<P>(P parameters);
|
public delegate IDictionary<string, object> AVRPCSerialize<P>(P parameters);
|
||||||
|
|
||||||
public AVRPCCloudFunctionBase()
|
public AVRPCCloudFunctionBase()
|
||||||
: this(true)
|
: this(true) {
|
||||||
{
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public AVRPCCloudFunctionBase(bool noneParameters)
|
public AVRPCCloudFunctionBase(bool noneParameters) {
|
||||||
{
|
if (noneParameters) {
|
||||||
if (noneParameters)
|
this.Encode = n => {
|
||||||
{
|
|
||||||
this.Encode = n =>
|
|
||||||
{
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -438,28 +393,21 @@ namespace LeanCloud {
|
||||||
|
|
||||||
|
|
||||||
private AVRPCDeserialize<R> _decode;
|
private AVRPCDeserialize<R> _decode;
|
||||||
public AVRPCDeserialize<R> Decode
|
public AVRPCDeserialize<R> Decode {
|
||||||
{
|
get {
|
||||||
get
|
|
||||||
{
|
|
||||||
return _decode;
|
return _decode;
|
||||||
}
|
}
|
||||||
set
|
set {
|
||||||
{
|
|
||||||
_decode = value;
|
_decode = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private AVRPCSerialize<P> _encode;
|
private AVRPCSerialize<P> _encode;
|
||||||
public AVRPCSerialize<P> Encode
|
public AVRPCSerialize<P> Encode {
|
||||||
{
|
get {
|
||||||
get
|
if (_encode == null) {
|
||||||
{
|
_encode = n => {
|
||||||
if (_encode == null)
|
|
||||||
{
|
|
||||||
_encode = n =>
|
|
||||||
{
|
|
||||||
if (n != null) {
|
if (n != null) {
|
||||||
return JsonConvert.DeserializeObject<Dictionary<string, object>>(n.ToString(), new LeanCloudJsonConverter());
|
return JsonConvert.DeserializeObject<Dictionary<string, object>>(n.ToString(), new LeanCloudJsonConverter());
|
||||||
}
|
}
|
||||||
|
@ -468,18 +416,15 @@ namespace LeanCloud {
|
||||||
}
|
}
|
||||||
return _encode;
|
return _encode;
|
||||||
}
|
}
|
||||||
set
|
set {
|
||||||
{
|
|
||||||
_encode = value;
|
_encode = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
public string FunctionName { get; set; }
|
public string FunctionName { get; set; }
|
||||||
|
|
||||||
public Task<R> ExecuteAsync(P parameters)
|
public Task<R> ExecuteAsync(P parameters) {
|
||||||
{
|
return AVUser.GetCurrentAsync().OnSuccess(t => {
|
||||||
return AVUser.GetCurrentAsync().OnSuccess(t =>
|
|
||||||
{
|
|
||||||
var user = t.Result;
|
var user = t.Result;
|
||||||
var encodedParameters = Encode(parameters);
|
var encodedParameters = Encode(parameters);
|
||||||
var command = new EngineCommand {
|
var command = new EngineCommand {
|
||||||
|
@ -488,11 +433,9 @@ namespace LeanCloud {
|
||||||
Content = encodedParameters
|
Content = encodedParameters
|
||||||
};
|
};
|
||||||
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command);
|
return AVPlugins.Instance.CommandRunner.RunCommandAsync<IDictionary<string, object>>(command);
|
||||||
}).Unwrap().OnSuccess(s =>
|
}).Unwrap().OnSuccess(s => {
|
||||||
{
|
|
||||||
var responseBody = s.Result.Item2;
|
var responseBody = s.Result.Item2;
|
||||||
if (!responseBody.ContainsKey("result"))
|
if (!responseBody.ContainsKey("result")) {
|
||||||
{
|
|
||||||
return default(R);
|
return default(R);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -503,31 +446,24 @@ namespace LeanCloud {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public class AVObjectRPCCloudFunction : AVObjectRPCCloudFunction<AVObject>
|
public class AVObjectRPCCloudFunction : AVObjectRPCCloudFunction<AVObject> {
|
||||||
{
|
|
||||||
|
|
||||||
}
|
}
|
||||||
public class AVObjectListRPCCloudFunction : AVObjectListRPCCloudFunction<AVObject>
|
public class AVObjectListRPCCloudFunction : AVObjectListRPCCloudFunction<AVObject> {
|
||||||
{
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AVObjectListRPCCloudFunction<R> : AVRPCCloudFunctionBase<object, IList<R>> where R : AVObject
|
public class AVObjectListRPCCloudFunction<R> : AVRPCCloudFunctionBase<object, IList<R>> where R : AVObject {
|
||||||
{
|
|
||||||
public AVObjectListRPCCloudFunction()
|
public AVObjectListRPCCloudFunction()
|
||||||
: base(true)
|
: base(true) {
|
||||||
{
|
|
||||||
this.Decode = this.AVObjectListDeserializer();
|
this.Decode = this.AVObjectListDeserializer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public AVRPCDeserialize<IList<R>> AVObjectListDeserializer()
|
public AVRPCDeserialize<IList<R>> AVObjectListDeserializer() {
|
||||||
{
|
AVRPCDeserialize<IList<R>> del = data => {
|
||||||
AVRPCDeserialize<IList<R>> del = data =>
|
|
||||||
{
|
|
||||||
var items = data["result"] as IList<object>;
|
var items = data["result"] as IList<object>;
|
||||||
|
|
||||||
return items.Select(item =>
|
return items.Select(item => {
|
||||||
{
|
|
||||||
var state = AVObjectCoder.Instance.Decode(item as IDictionary<string, object>, AVDecoder.Instance);
|
var state = AVObjectCoder.Instance.Decode(item as IDictionary<string, object>, AVDecoder.Instance);
|
||||||
return AVObject.FromState<AVObject>(state, state.ClassName);
|
return AVObject.FromState<AVObject>(state, state.ClassName);
|
||||||
}).ToList() as IList<R>;
|
}).ToList() as IList<R>;
|
||||||
|
@ -537,19 +473,15 @@ namespace LeanCloud {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AVObjectRPCCloudFunction<R> : AVRPCCloudFunctionBase<object, R> where R : AVObject
|
public class AVObjectRPCCloudFunction<R> : AVRPCCloudFunctionBase<object, R> where R : AVObject {
|
||||||
{
|
|
||||||
public AVObjectRPCCloudFunction()
|
public AVObjectRPCCloudFunction()
|
||||||
: base(true)
|
: base(true) {
|
||||||
{
|
|
||||||
this.Decode = this.AVObjectDeserializer();
|
this.Decode = this.AVObjectDeserializer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public AVRPCDeserialize<R> AVObjectDeserializer()
|
public AVRPCDeserialize<R> AVObjectDeserializer() {
|
||||||
{
|
AVRPCDeserialize<R> del = data => {
|
||||||
AVRPCDeserialize<R> del = data =>
|
|
||||||
{
|
|
||||||
var item = data["result"] as object;
|
var item = data["result"] as object;
|
||||||
var state = AVObjectCoder.Instance.Decode(item as IDictionary<string, object>, AVDecoder.Instance);
|
var state = AVObjectCoder.Instance.Decode(item as IDictionary<string, object>, AVDecoder.Instance);
|
||||||
|
|
||||||
|
|
|
@ -1,143 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using LeanCloud.Storage.Internal;
|
|
||||||
using LeanCloud.Utilities;
|
|
||||||
|
|
||||||
namespace LeanCloud
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The AVConfig is a representation of the remote configuration object,
|
|
||||||
/// that enables you to add things like feature gating, a/b testing or simple "Message of the day".
|
|
||||||
/// </summary>
|
|
||||||
public class AVConfig : IJsonConvertible
|
|
||||||
{
|
|
||||||
private IDictionary<string, object> properties = new Dictionary<string, object>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the latest fetched AVConfig.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>AVConfig object</returns>
|
|
||||||
public static AVConfig CurrentConfig
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
Task<AVConfig> task = ConfigController.CurrentConfigController.GetCurrentConfigAsync();
|
|
||||||
task.Wait();
|
|
||||||
return task.Result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void ClearCurrentConfig()
|
|
||||||
{
|
|
||||||
ConfigController.CurrentConfigController.ClearCurrentConfigAsync().Wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void ClearCurrentConfigInMemory()
|
|
||||||
{
|
|
||||||
ConfigController.CurrentConfigController.ClearCurrentConfigInMemoryAsync().Wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IAVConfigController ConfigController
|
|
||||||
{
|
|
||||||
get { return AVPlugins.Instance.ConfigController; }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal AVConfig()
|
|
||||||
: base()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
internal AVConfig(IDictionary<string, object> fetchedConfig)
|
|
||||||
{
|
|
||||||
var props = AVDecoder.Instance.Decode(fetchedConfig["params"]) as IDictionary<string, object>;
|
|
||||||
properties = props;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieves the AVConfig asynchronously from the server.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>AVConfig object that was fetched</returns>
|
|
||||||
public static Task<AVConfig> GetAsync()
|
|
||||||
{
|
|
||||||
return GetAsync(CancellationToken.None);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieves the AVConfig asynchronously from the server.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
/// <returns>AVConfig object that was fetched</returns>
|
|
||||||
public static Task<AVConfig> GetAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return ConfigController.FetchConfigAsync(AVUser.CurrentSessionToken, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value for the key of a particular type.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type to convert the value to. Supported types are
|
|
||||||
/// AVObject and its descendents, LeanCloud types such as AVRelation and AVGeopoint,
|
|
||||||
/// primitive types,IList<T>, IDictionary<string, T> and strings.</typeparam>
|
|
||||||
/// <param name="key">The key of the element to get.</param>
|
|
||||||
/// <exception cref="System.Collections.Generic.KeyNotFoundException">The property is retrieved
|
|
||||||
/// and <paramref name="key"/> is not found.</exception>
|
|
||||||
/// <exception cref="System.FormatException">The property under this <paramref name="key"/>
|
|
||||||
/// key was found, but of a different type.</exception>
|
|
||||||
public T Get<T>(string key)
|
|
||||||
{
|
|
||||||
return Conversion.To<T>(this.properties[key]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Populates result with the value for the key, if possible.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The desired type for the value.</typeparam>
|
|
||||||
/// <param name="key">The key to retrieve a value for.</param>
|
|
||||||
/// <param name="result">The value for the given key, converted to the
|
|
||||||
/// requested type, or null if unsuccessful.</param>
|
|
||||||
/// <returns>true if the lookup and conversion succeeded, otherwise false.</returns>
|
|
||||||
public bool TryGetValue<T>(string key, out T result)
|
|
||||||
{
|
|
||||||
if (this.properties.ContainsKey(key))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var temp = Conversion.To<T>(this.properties[key]);
|
|
||||||
result = temp;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
// Could not convert, do nothing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result = default(T);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value on the config.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key">The key for the parameter.</param>
|
|
||||||
/// <exception cref="System.Collections.Generic.KeyNotFoundException">The property is
|
|
||||||
/// retrieved and <paramref name="key"/> is not found.</exception>
|
|
||||||
/// <returns>The value for the key.</returns>
|
|
||||||
virtual public object this[string key]
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return this.properties[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IDictionary<string, object> IJsonConvertible.ToJSON()
|
|
||||||
{
|
|
||||||
return new Dictionary<string, object> {
|
|
||||||
{ "params", NoObjectsEncoder.Instance.Encode(properties) }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -272,7 +272,7 @@ namespace LeanCloud
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static IAVFileController FileController
|
internal static AVFileController FileController
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -37,13 +37,13 @@ namespace LeanCloud
|
||||||
internal int? skip;
|
internal int? skip;
|
||||||
internal int? limit;
|
internal int? limit;
|
||||||
|
|
||||||
internal static IAVQueryController QueryController {
|
internal static AVQueryController QueryController {
|
||||||
get {
|
get {
|
||||||
return AVPlugins.Instance.QueryController;
|
return AVPlugins.Instance.QueryController;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static IObjectSubclassingController SubclassingController {
|
internal static ObjectSubclassingController SubclassingController {
|
||||||
get {
|
get {
|
||||||
return AVPlugins.Instance.SubclassingController;
|
return AVPlugins.Instance.SubclassingController;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ namespace LeanCloud
|
||||||
this.targetClassName = targetClassName;
|
this.targetClassName = targetClassName;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static IObjectSubclassingController SubclassingController
|
internal static ObjectSubclassingController SubclassingController
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
|
|
@ -41,7 +41,7 @@ namespace LeanCloud
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static IAVSessionController SessionController
|
internal static AVSessionController SessionController
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
|
|
@ -20,7 +20,7 @@ namespace LeanCloud
|
||||||
"sessionToken", "isNew"
|
"sessionToken", "isNew"
|
||||||
};
|
};
|
||||||
|
|
||||||
internal static IAVUserController UserController
|
internal static AVUserController UserController
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
@ -28,7 +28,7 @@ namespace LeanCloud
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static IAVCurrentUserController CurrentUserController
|
internal static AVCurrentUserController CurrentUserController
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue