* AppRouterController.cs: chore:

* Utils.cs:
* RoleTest.cs:
* AVQuery.cs:
* AVStatus.cs:
* AVRelation.cs:
* AVFieldNameAttribute.cs:

* AVRole.cs: chore: 测试 Role 模块
oneRain 2019-08-27 15:19:19 +08:00
parent 56bfee1732
commit 9266d6e115
8 changed files with 113 additions and 151 deletions

View File

@ -34,7 +34,7 @@ namespace LeanCloud.Storage.Internal {
}
}
public async Task<AppRouterState> QueryAsync(string appId) {
async Task<AppRouterState> QueryAsync(string appId) {
Console.WriteLine("QueryAsync");
string url = string.Format("https://app-router.leancloud.cn/2/route?appId={0}", appId);

View File

@ -0,0 +1,27 @@
using NUnit.Framework;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using LeanCloud;
namespace LeanCloudTests {
public class RoleTest {
[SetUp]
public void SetUp() {
Utils.InitNorthChina(true);
}
[Test]
public async Task GetUsersFromRole() {
AVQuery<AVRole> query = new AVQuery<AVRole>();
AVRole role = await query.FirstAsync();
AVQuery<AVUser> userQuery = role.Users.Query;
IEnumerable<AVUser> users = await userQuery.FindAsync();
Assert.Greater(users.Count(), 0);
TestContext.Out.WriteLine($"count: {users.Count()}");
foreach (AVUser user in users) {
TestContext.Out.WriteLine($"{user.ObjectId}, {user.Username}");
}
}
}
}

View File

@ -4,30 +4,38 @@ using NUnit.Framework;
namespace LeanCloudTests {
public static class Utils {
public static void InitNorthChina() {
AVClient.Initialize(new AVClient.Configuration {
ApplicationId = "BMYV4RKSTwo8WSqt8q9ezcWF-gzGzoHsz",
ApplicationKey = "pbf6Nk5seyjilexdpyrPwjSp",
ApiServer = "https://avoscloud.com"
});
AVClient.HttpLog(TestContext.Out.WriteLine);
public static void InitNorthChina(bool master = false) {
if (master) {
Init("BMYV4RKSTwo8WSqt8q9ezcWF-gzGzoHsz", "pbf6Nk5seyjilexdpyrPwjSp", "https://avoscloud.com", "qKH9ryRagHKvXeRRVkiUiHeb");
} else {
Init("BMYV4RKSTwo8WSqt8q9ezcWF-gzGzoHsz", "pbf6Nk5seyjilexdpyrPwjSp", "https://avoscloud.com");
}
}
public static void InitEastChina() {
AVClient.Initialize(new AVClient.Configuration {
ApplicationId = "4eTwHdYhMaNBUpl1SrTr7GLC-9Nh9j0Va",
ApplicationKey = "GSD6DtdgGWlWolivN4qhWtlE",
ApiServer = "https://4eTwHdYh.api.lncldapi.com"
});
AVClient.HttpLog(TestContext.Out.WriteLine);
public static void InitEastChina(bool master = false) {
if (master) {
Init("4eTwHdYhMaNBUpl1SrTr7GLC-9Nh9j0Va", "GSD6DtdgGWlWolivN4qhWtlE", "https://4eTwHdYh.api.lncldapi.com", "eqEp4n89h4zanWFskDDpIwL4");
} else {
Init("4eTwHdYhMaNBUpl1SrTr7GLC-9Nh9j0Va", "GSD6DtdgGWlWolivN4qhWtlE", "https://4eTwHdYh.api.lncldapi.com");
}
}
public static void InitUS() {
public static void InitUS(bool master = false) {
if (master) {
Init("MFAS1GnOyomRLSQYRaxdgdPz-MdYXbMMI", "p42JUxdxb95K5G8187t5ba3l", "https://MFAS1GnO.api.lncldglobal.com", "Ahb1wdFLwMgKwEaEicHRXbCY");
} else {
Init("MFAS1GnOyomRLSQYRaxdgdPz-MdYXbMMI", "p42JUxdxb95K5G8187t5ba3l", "https://MFAS1GnO.api.lncldglobal.com");
}
}
static void Init(string appId, string appKey, string apiServer, string masterKey = null) {
AVClient.Initialize(new AVClient.Configuration {
ApplicationId = "MFAS1GnOyomRLSQYRaxdgdPz-MdYXbMMI",
ApplicationKey = "p42JUxdxb95K5G8187t5ba3l",
ApiServer = "https://MFAS1GnO.api.lncldglobal.com"
ApplicationId = appId,
ApplicationKey = appKey,
MasterKey = masterKey,
ApiServer = apiServer
});
AVClient.UseMasterKey = !string.IsNullOrEmpty(masterKey);
AVClient.HttpLog(TestContext.Out.WriteLine);
}
}

View File

@ -1,24 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LeanCloud
{
namespace LeanCloud {
/// <summary>
/// Specifies a field name for a property on a AVObject subclass.
/// </summary>
[AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)]
public sealed class AVFieldNameAttribute : Attribute
{
public sealed class AVFieldNameAttribute : Attribute {
/// <summary>
/// Constructs a new AVFieldName attribute.
/// </summary>
/// <param name="fieldName">The name of the field on the AVObject that the
/// property represents.</param>
public AVFieldNameAttribute(string fieldName)
{
public AVFieldNameAttribute(string fieldName) {
FieldName = fieldName;
}

View File

@ -219,7 +219,7 @@ namespace LeanCloud
});
}
public Task<T> FirstAsync(CancellationToken cancellationToken)
public Task<T> FirstAsync(CancellationToken cancellationToken = default)
{
return FirstOrDefaultAsync(cancellationToken).OnSuccess(t =>
{

View File

@ -8,71 +8,59 @@ using System.Linq.Expressions;
using System.Reflection;
using System.Text;
namespace LeanCloud
{
namespace LeanCloud {
/// <summary>
/// A common base class for AVRelations.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public abstract class AVRelationBase : IJsonConvertible
{
public abstract class AVRelationBase : IJsonConvertible {
private AVObject parent;
private string key;
private string targetClassName;
internal AVRelationBase(AVObject parent, string key)
{
internal AVRelationBase(AVObject parent, string key) {
EnsureParentAndKey(parent, key);
}
internal AVRelationBase(AVObject parent, string key, string targetClassName)
: this(parent, key)
{
: this(parent, key) {
this.targetClassName = targetClassName;
}
internal static ObjectSubclassingController SubclassingController
{
get
{
internal static ObjectSubclassingController SubclassingController {
get {
return AVPlugins.Instance.SubclassingController;
}
}
internal void EnsureParentAndKey(AVObject parent, string key)
{
internal void EnsureParentAndKey(AVObject parent, string key) {
this.parent = this.parent ?? parent;
this.key = this.key ?? key;
Debug.Assert(this.parent == parent, "Relation retrieved from two different objects");
Debug.Assert(this.key == key, "Relation retrieved from two different keys");
}
internal void Add(AVObject obj)
{
internal void Add(AVObject obj) {
var change = new AVRelationOperation(new[] { obj }, null);
parent.PerformOperation(key, change);
targetClassName = change.TargetClassName;
}
internal void Remove(AVObject obj)
{
internal void Remove(AVObject obj) {
var change = new AVRelationOperation(null, new[] { obj });
parent.PerformOperation(key, change);
targetClassName = change.TargetClassName;
}
IDictionary<string, object> IJsonConvertible.ToJSON()
{
IDictionary<string, object> IJsonConvertible.ToJSON() {
return new Dictionary<string, object> {
{ "__type", "Relation"},
{ "className", targetClassName}
};
}
internal AVQuery<T> GetQuery<T>() where T : AVObject
{
if (targetClassName != null)
{
internal AVQuery<T> GetQuery<T>() where T : AVObject {
if (targetClassName != null) {
return new AVQuery<T>(targetClassName)
.WhereRelatedTo(parent, key);
}
@ -82,24 +70,19 @@ namespace LeanCloud
.WhereRelatedTo(parent, key);
}
internal AVQuery<T> GetReverseQuery<T>(T target) where T : AVObject
{
if (target.ObjectId == null)
{
throw new ArgumentNullException("target.ObjectId", "can not query a relation without target ObjectId.");
internal AVQuery<T> GetReverseQuery<T>(T target) where T : AVObject {
if (target.ObjectId == null) {
throw new ArgumentNullException(nameof(target), "can not query a relation without target ObjectId.");
}
return new AVQuery<T>(parent.ClassName).WhereEqualTo(key, target);
}
internal string TargetClassName
{
get
{
internal string TargetClassName {
get {
return targetClassName;
}
set
{
set {
targetClassName = value;
}
}
@ -109,8 +92,7 @@ namespace LeanCloud
/// </summary>
internal static AVRelationBase CreateRelation(AVObject parent,
string key,
string targetClassName)
{
string targetClassName) {
var targetType = SubclassingController.GetType(targetClassName) ?? typeof(AVObject);
Expression<Func<AVRelation<AVObject>>> createRelationExpr =
@ -124,8 +106,7 @@ namespace LeanCloud
}
private static AVRelation<T> CreateRelation<T>(AVObject parent, string key, string targetClassName)
where T : AVObject
{
where T : AVObject {
return new AVRelation<T>(parent, key, targetClassName);
}
}
@ -135,8 +116,7 @@ namespace LeanCloud
/// AVRelation is associated with a particular parent and key.
/// </summary>
/// <typeparam name="T">The type of the child objects.</typeparam>
public sealed class AVRelation<T> : AVRelationBase where T : AVObject
{
public sealed class AVRelation<T> : AVRelationBase where T : AVObject {
internal AVRelation(AVObject parent, string key) : base(parent, key) { }
@ -147,8 +127,7 @@ namespace LeanCloud
/// Adds an object to this relation. The object must already have been saved.
/// </summary>
/// <param name="obj">The object to add.</param>
public void Add(T obj)
{
public void Add(T obj) {
base.Add(obj);
}
@ -156,18 +135,15 @@ namespace LeanCloud
/// Removes an object from this relation. The object must already have been saved.
/// </summary>
/// <param name="obj">The object to remove.</param>
public void Remove(T obj)
{
public void Remove(T obj) {
base.Remove(obj);
}
/// <summary>
/// Gets a query that can be used to query the objects in this relation.
/// </summary>
public AVQuery<T> Query
{
get
{
public AVQuery<T> Query {
get {
return base.GetQuery<T>();
}
}

View File

@ -1,32 +1,18 @@
using LeanCloud.Storage.Internal;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace LeanCloud
{
namespace LeanCloud {
/// <summary>
/// Represents a Role on the LeanCloud server. AVRoles represent groupings
/// of <see cref="AVUser"/>s for the purposes of granting permissions (e.g.
/// specifying a <see cref="AVACL"/> for a <see cref="AVObject"/>. Roles
/// are specified by their sets of child users and child roles, all of which are granted
/// any permissions that the parent role has.
///
/// Roles must have a name (that cannot be changed after creation of the role),
/// and must specify an ACL.
/// 角色类
/// </summary>
[AVClassName("_Role")]
public class AVRole : AVObject
{
public class AVRole : AVObject {
private static readonly Regex namePattern = new Regex("^[0-9a-zA-Z_\\- ]+$");
/// <summary>
/// Constructs a new AVRole. You must assign a name and ACL to the role.
/// </summary>
public AVRole() : base() { }
public AVRole() { }
/// <summary>
/// Constructs a new AVRole with the given name.
@ -34,8 +20,7 @@ namespace LeanCloud
/// <param name="name">The name of the role to create.</param>
/// <param name="acl">The ACL for this role. Roles must have an ACL.</param>
public AVRole(string name, AVACL acl)
: this()
{
: this() {
Name = name;
ACL = acl;
}
@ -44,10 +29,13 @@ namespace LeanCloud
/// Gets the name of the role.
/// </summary>
[AVFieldName("name")]
public string Name
{
get { return GetProperty<string>("Name"); }
set { SetProperty(value, "Name"); }
public string Name {
get {
return GetProperty<string>("Name");
}
set {
SetProperty(value, "Name");
}
}
/// <summary>
@ -57,9 +45,10 @@ namespace LeanCloud
/// add or remove child users from the role through this relation.
/// </summary>
[AVFieldName("users")]
public AVRelation<AVUser> Users
{
get { return GetRelationProperty<AVUser>("Users"); }
public AVRelation<AVUser> Users {
get {
return GetRelationProperty<AVUser>("Users");
}
}
/// <summary>
@ -69,30 +58,25 @@ namespace LeanCloud
/// add or remove child roles from the role through this relation.
/// </summary>
[AVFieldName("roles")]
public AVRelation<AVRole> Roles
{
get { return GetRelationProperty<AVRole>("Roles"); }
public AVRelation<AVRole> Roles {
get {
return GetRelationProperty<AVRole>("Roles");
}
}
internal override void OnSettingValue(ref string key, ref object value)
{
internal override void OnSettingValue(ref string key, ref object value) {
base.OnSettingValue(ref key, ref value);
if (key == "name")
{
if (ObjectId != null)
{
if (key == "name") {
if (ObjectId != null) {
throw new InvalidOperationException(
"A role's name can only be set before it has been saved.");
}
if (!(value is string))
{
throw new ArgumentException("A role's name must be a string.", "value");
if (!(value is string)) {
throw new ArgumentException("A role's name must be a string.", nameof(value));
}
if (!namePattern.IsMatch((string)value))
{
if (!namePattern.IsMatch((string)value)) {
throw new ArgumentException(
"A role's name can only contain alphanumeric characters, _, -, and spaces.",
"value");
"A role's name can only contain alphanumeric characters, _, -, and spaces.", nameof(value));
}
}
}
@ -100,10 +84,8 @@ namespace LeanCloud
/// <summary>
/// Gets a <see cref="AVQuery{AVRole}"/> over the Role collection.
/// </summary>
public static AVQuery<AVRole> Query
{
get
{
public static AVQuery<AVRole> Query {
get {
return new AVQuery<AVRole>();
}
}

View File

@ -1,24 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LeanCloud
{
/// <summary>
/// 事件流系统中的一条状态
/// </summary>
[AVClassName("_Status")]
public class AVStatus : AVObject
{
private static readonly HashSet<string> readOnlyKeys = new HashSet<string> {
"messageId", "inboxType", "data","Source"
};
protected override bool IsKeyMutable(string key)
{
return !readOnlyKeys.Contains(key);
}
}
}