chore: 简化

oneRain 2019-08-07 16:35:21 +08:00
parent 905cc943bf
commit 81b2ea993f
45 changed files with 713 additions and 2133 deletions

View File

@ -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;
} }
} }

View File

@ -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.");

View File

@ -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();

View File

@ -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();
}
}

View File

@ -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"))

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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));
}
}

View File

@ -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();
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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);
} }

View File

@ -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 {

View File

@ -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);
}
}

View File

@ -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;
} }

View File

@ -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)
{ {

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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; }
}
}

View File

@ -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();
}
}

View File

@ -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);
}
} }
}
} }

View File

@ -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) {

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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();

View File

@ -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;
// });
//}
} }
} }

View File

@ -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;
}
}

View File

@ -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);
}); });
} }

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
} }

View File

@ -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"));
} }
} }

View File

@ -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,

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -21,12 +21,9 @@ namespace LeanCloud {
/// await AVCloud.CallFunctionAsync&lt;IDictionary&lt;string, object&gt;&gt;("validateGame", parameters); /// await AVCloud.CallFunctionAsync&lt;IDictionary&lt;string, object&gt;&gt;("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);

View File

@ -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&lt;T&gt;, IDictionary&lt;string, T&gt; 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) }
};
}
}
}

View File

@ -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

View File

@ -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;
} }

View File

@ -31,7 +31,7 @@ namespace LeanCloud
this.targetClassName = targetClassName; this.targetClassName = targetClassName;
} }
internal static IObjectSubclassingController SubclassingController internal static ObjectSubclassingController SubclassingController
{ {
get get
{ {

View File

@ -41,7 +41,7 @@ namespace LeanCloud
} }
} }
internal static IAVSessionController SessionController internal static AVSessionController SessionController
{ {
get get
{ {

View File

@ -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
{ {