Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat:Support public properties in MinimalAPIs #554

Merged
merged 5 commits into from
Apr 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ public async Task TestGetWorkerIdAsync()
[TestMethod]
public async Task TestGetDistibutedLockFaieldAsync()
{
var workerIdBits = 10;
var workerIdBits = 2;
var maxWorkerId = ~(-1L << workerIdBits);
var tasks = new ConcurrentBag<Task>();
ThreadPool.GetMinThreads(out int workerThreads, out var minIoc);
Expand All @@ -242,7 +242,7 @@ public async Task TestGetDistibutedLockFaieldAsync()
int laterTime = 0;
try
{
Parallel.For(0, maxWorkerId * 2, _ =>
Parallel.For(0, maxWorkerId * 10, _ =>
{
tasks.Add(GetWorkerIdAsync(null, workerIdBits));
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ public static Delegate CreateDelegate(MethodInfo methodInfo, object targetInstan
{
var type = Expression.GetDelegateType(methodInfo.GetParameters().Select(parameterInfo => parameterInfo.ParameterType)
.Concat(new List<Type>
{ methodInfo.ReturnType }).ToArray());
{
methodInfo.ReturnType
}).ToArray());
return Delegate.CreateDelegate(type, targetInstance, methodInfo);
}

Expand All @@ -24,7 +26,7 @@ public static string ParseMethodPrefix(IEnumerable<string> prefixes, string meth
var prefix = prefixes.FirstOrDefault(prefix => newMethodName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase));

if (prefix is not null)
return prefix;
return methodName.Substring(0, prefix.Length);

return string.Empty;
}
Expand Down
42 changes: 32 additions & 10 deletions src/Contrib/Service/Masa.Contrib.Service.MinimalAPIs/ServiceBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ public abstract class ServiceBase : IService

public IServiceCollection Services => MasaApp.GetServices();

private bool? _enableProperty;

#pragma warning disable S4136
protected ServiceBase() { }

Expand All @@ -50,14 +52,7 @@ protected virtual IServiceProvider GetServiceProvider()

internal void AutoMapRoute(ServiceGlobalRouteOptions globalOptions, PluralizationService pluralizationService)
{
var type = GetType();

var methodInfos = type
.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance)
.Where(methodInfo => methodInfo.CustomAttributes.All(attr => attr.AttributeType != typeof(IgnoreRouteAttribute)))
.Concat(type.GetMethods(BindingFlags.Public | BindingFlags.Instance)
.Where(methodInfo => methodInfo.CustomAttributes.Any(attr => attr.AttributeType == typeof(RoutePatternAttribute))))
.Distinct();
var methodInfos = GetMethodsByAutoMapRoute(GetType(), globalOptions);

foreach (var method in methodInfos)
{
Expand Down Expand Up @@ -92,6 +87,25 @@ internal void AutoMapRoute(ServiceGlobalRouteOptions globalOptions, Pluralizatio
}
}

protected virtual List<MethodInfo> GetMethodsByAutoMapRoute(Type type, ServiceGlobalRouteOptions globalOptions)
{
var bindingFlags = BindingFlags.Public | BindingFlags.Instance;
var methodInfos = type
.GetMethods(BindingFlags.DeclaredOnly | bindingFlags)
.Where(methodInfo => methodInfo.CustomAttributes.All(attr => attr.AttributeType != typeof(IgnoreRouteAttribute)))
.Concat(type.GetMethods(bindingFlags)
.Where(methodInfo => methodInfo.CustomAttributes.Any(attr => attr.AttributeType == typeof(RoutePatternAttribute))))
.Distinct();
_enableProperty = RouteOptions.EnableProperty ?? globalOptions.EnableProperty ?? false;
if (!_enableProperty.Value)
{
return methodInfos.Where(methodInfo => !methodInfo.IsSpecialName).ToList();
}

return methodInfos.Where(methodInfo
=> !methodInfo.IsSpecialName || (methodInfo.IsSpecialName && methodInfo.Name.StartsWith("get_"))).ToList();
}

protected virtual string GetBaseUri(ServiceRouteOptions globalOptions, PluralizationService pluralizationService)
{
if (!string.IsNullOrWhiteSpace(BaseUri))
Expand All @@ -112,7 +126,10 @@ protected virtual string GetBaseUri(ServiceRouteOptions globalOptions, Pluraliza
RouteHandlerBuilder MapMethods(ServiceRouteOptions globalOptions, string pattern, string? httpMethod, Delegate handler)
{
if (!string.IsNullOrWhiteSpace(httpMethod))
return App.MapMethods(pattern, new[] { httpMethod }, handler);
return App.MapMethods(pattern, new[]
{
httpMethod
}, handler);

var httpMethods = GetDefaultHttpMethods(globalOptions);
if (httpMethods.Length > 0)
Expand Down Expand Up @@ -173,7 +190,12 @@ string TrimMethodPrefix(string name)

protected virtual (string? HttpMethod, string Prefix) ParseMethod(ServiceRouteOptions globalOptions, string methodName)
{
var prefix = ServiceBaseHelper.ParseMethodPrefix(RouteOptions.GetPrefixes ?? globalOptions.GetPrefixes!, methodName);
var getPrefixes = RouteOptions.GetPrefixes ?? globalOptions.GetPrefixes!;
if (_enableProperty!.Value)
{
getPrefixes.Insert(0, "get_");
}
var prefix = ServiceBaseHelper.ParseMethodPrefix(getPrefixes, methodName);
if (!string.IsNullOrEmpty(prefix))
return ("GET", prefix);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ public ServiceGlobalRouteOptions()
DisableTrimMethodPrefix = false;
Assemblies = MasaApp.GetAssemblies();
Pluralization = PluralizationService.CreateService(CultureInfo.CreateSpecificCulture("en"));
EnableProperty = false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,10 @@ public class ServiceRouteOptions
/// When the collection is empty, the default Post, Get, Put, Delete all support access
/// </summary>
public string[] MapHttpMethodsForUnmatched { get; set; } = Array.Empty<string>();

/// <summary>
/// Enable access to public properties
/// default: false
/// </summary>
public bool? EnableProperty { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ namespace Masa.Contrib.Service.MinimalAPIs.Tests;
[TestClass]
public class ServiceBaseTest
{
private static FieldInfo _enablePropertyFieldInfo
=> typeof(ServiceBase).GetField("_enableProperty", BindingFlags.Instance | BindingFlags.NonPublic)!;

[TestMethod]
public void TestGetBaseUri()
{
Expand Down Expand Up @@ -40,9 +43,7 @@ public void TestCombineUris()
{
var uris = new[]
{
"api",
"v1",
"order"
"api", "v1", "order"
};
Assert.AreEqual("api/v1/order", ServiceBaseHelper.CombineUris(uris));
}
Expand Down Expand Up @@ -154,18 +155,26 @@ public void TestConstructor()
}

[DataTestMethod]
[DataRow("AddUser", "POST", "Add")]
[DataRow("PostUser", "POST", "Post")]
[DataRow("DeleteUser", "DELETE", "Delete")]
[DataRow("PutUser", "PUT", "Put")]
[DataRow("GetUser", "GET", "Get")]
[DataRow("AuditState", null, "")]
[DataRow("AddUser", "POST", "Add", false)]
[DataRow("PostUser", "POST", "Post", false)]
[DataRow("DeleteUser", "DELETE", "Delete", false)]
[DataRow("PutUser", "PUT", "Put", false)]
[DataRow("GetUser", "GET", "Get", false)]
[DataRow("get_Name", "GET", "get", false)]
[DataRow("get_Name", "GET", "get_", true)]
[DataRow("set_Name", null, "", false)]
[DataRow("set_Name", null, "", true)]
[DataRow("AuditState", null, "", false)]
public void TestParseMethod(
string methodName,
string? actualHttpMethod,
string actualPrefix)
string actualPrefix,
bool enableProperty)
{
var service = new UserService();

_enablePropertyFieldInfo.SetValue(service, enableProperty);

var globalOptions = new ServiceGlobalRouteOptions();
var result = service.TestParseMethod(globalOptions, methodName);
Assert.AreEqual(actualHttpMethod, result.HttpMethod);
Expand Down Expand Up @@ -202,6 +211,26 @@ public void TestGetServiceName(bool enablePluralizeServiceName, string actualSer
Assert.AreEqual("Catalogs", service.TestGetServiceName(pluralizationService));
}

[DataTestMethod]
[DataRow(true, true, 2)]
[DataRow(true, false, 1)]
[DataRow(true, null, 2)]
[DataRow(null, true, 2)]
[DataRow(null, false, 1)]
[DataRow(null, null, 1)]
[DataRow(false, true, 2)]
[DataRow(false, false, 1)]
[DataRow(false, null, 1)]
public void TestGetMethodsByAutoMapRoute(bool? globalEnableProperty, bool? enableProperty, int expectedNumber)
{
var orderService = new OrderService(enableProperty);
var methodInfos = orderService.TestGetMethodsByAutoMapRoute(new ServiceGlobalRouteOptions()
{
EnableProperty = globalEnableProperty
});
Assert.AreEqual(expectedNumber, methodInfos.Count);
}

#region private methods

private static CustomServiceBase GetCustomService()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) MASA Stack All rights reserved.
// Licensed under the MIT License. See LICENSE.txt in the project root for license information.

namespace Masa.Contrib.Service.MinimalAPIs.Tests.Services;

#pragma warning disable CA1822
public class OrderService : ServiceBase
{
public string ConnectionString => GetConnectionString;

public static string GetConnectionString => "connection string";

public int Id { private get; set; }

private int Age { get; set; }

public int CreateTime;

public OrderService() : base()
{

}

public OrderService(bool? enableProperty)
{
RouteOptions.EnableProperty = enableProperty;
}

private static string GetName() => "name";

public static void SetName()
{

}

public override string ToString()
{
return nameof(OrderService);
}

[IgnoreRoute]
public List<MethodInfo> TestGetMethodsByAutoMapRoute(ServiceGlobalRouteOptions globalOptions)
{
return base.GetMethodsByAutoMapRoute(typeof(OrderService), globalOptions);
}
}
#pragma warning restore CA1822