Browse Source

Added api models and aspnet core model provider.

pull/96/head
Halil İbrahim Kalkan 9 years ago
parent
commit
d96adba277
  1. 3
      src/Volo.Abp.AspNetCore.Mvc/Properties/AssemblyInfo.cs
  2. 1
      src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj
  3. 29
      src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ActionDescriptorExtensions.cs
  4. 149
      src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs
  5. 2
      src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ControllerAssemblySettingList.cs
  6. 42
      src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Utils/ArrayMatcher.cs
  7. 4
      src/Volo.Abp.Http/Volo.Abp.Http.csproj
  8. 40
      src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs
  9. 47
      src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ApplicationApiDescriptionModel.cs
  10. 53
      src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ControllerApiDescriptionModel.cs
  11. 7
      src/Volo.Abp.Http/Volo/Abp/Http/Modeling/IApiDescriptionModelProvider.cs
  12. 56
      src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ModuleApiDescriptionModel.cs
  13. 41
      src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ParameterApiDescriptionModel.cs
  14. 17
      src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ReturnValueApiDescriptionModel.cs
  15. 13
      src/Volo.Abp/System/AbpTypeExtensions.cs
  16. 2
      src/Volo.Abp/Volo/Abp/Modularity/AbpModuleDescriptor.cs
  17. 2
      src/Volo.Abp/Volo/Abp/Reflection/AssemblyFinder.cs
  18. 19
      test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Utils/ArrayMacther_Tests.cs
  19. 2
      test/Volo.Abp.Tests/Volo/Abp/Reflection/AssemblyFinder_Tests.cs
  20. 4
      test/Volo.Abp.Tests/Volo/Abp/Reflection/TypeFinder_Tests.cs

3
src/Volo.Abp.AspNetCore.Mvc/Properties/AssemblyInfo.cs

@ -1,4 +1,5 @@
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following // General Information about an assembly is controlled through the following
@ -9,6 +10,8 @@ using System.Runtime.InteropServices;
[assembly: AssemblyProduct("Volo.Abp.AspNetCore.Mvc")] [assembly: AssemblyProduct("Volo.Abp.AspNetCore.Mvc")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly:InternalsVisibleTo("Volo.Abp.AspNetCore.Mvc.Tests")]
// Setting ComVisible to false makes the types in this assembly not visible // Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from // to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type. // COM, set the ComVisible attribute to true on that type.

1
src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj

@ -13,6 +13,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Volo.Abp.AspNetCore\Volo.Abp.AspNetCore.csproj" /> <ProjectReference Include="..\Volo.Abp.AspNetCore\Volo.Abp.AspNetCore.csproj" />
<ProjectReference Include="..\Volo.Abp.Http\Volo.Abp.Http.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

29
src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ActionDescriptorExtensions.cs

@ -0,0 +1,29 @@
using System.Reflection;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Controllers;
namespace Volo.Abp.AspNetCore.Mvc
{
public static class ActionDescriptorExtensions
{
public static ControllerActionDescriptor AsControllerActionDescriptor(this ActionDescriptor actionDescriptor)
{
if (!actionDescriptor.IsControllerAction())
{
throw new AbpException($"{nameof(actionDescriptor)} should be type of {typeof(ControllerActionDescriptor).AssemblyQualifiedName}");
}
return actionDescriptor as ControllerActionDescriptor;
}
public static MethodInfo GetMethodInfo(this ActionDescriptor actionDescriptor)
{
return actionDescriptor.AsControllerActionDescriptor().MethodInfo;
}
public static bool IsControllerAction(this ActionDescriptor actionDescriptor)
{
return actionDescriptor is ControllerActionDescriptor;
}
}
}

149
src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AspNetCoreApiDescriptionModelProvider.cs

@ -0,0 +1,149 @@
using System;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Volo.Abp.Application.Services;
using Volo.Abp.AspNetCore.Mvc.Utils;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.Modeling;
namespace Volo.Abp.AspNetCore.Mvc
{
public class AspNetCoreApiDescriptionModelProvider : IApiDescriptionModelProvider, ISingletonDependency
{
public ILogger<AspNetCoreApiDescriptionModelProvider> Logger { get; set; }
private readonly IApiDescriptionGroupCollectionProvider _descriptionProvider;
private readonly AbpAspNetCoreMvcOptions _options;
public AspNetCoreApiDescriptionModelProvider(
IApiDescriptionGroupCollectionProvider descriptionProvider,
IOptions<AbpAspNetCoreMvcOptions> options)
{
_descriptionProvider = descriptionProvider;
_options = options.Value;
Logger = NullLogger<AspNetCoreApiDescriptionModelProvider>.Instance;
}
public ApplicationApiDescriptionModel CreateModel()
{
var model = new ApplicationApiDescriptionModel();
foreach (var descriptionGroupItem in _descriptionProvider.ApiDescriptionGroups.Items)
{
foreach (var apiDescription in descriptionGroupItem.Items)
{
if (!apiDescription.ActionDescriptor.IsControllerAction())
{
continue;
}
AddApiDescriptionToModel(apiDescription, model);
}
}
return model;
}
private void AddApiDescriptionToModel(ApiDescription apiDescription, ApplicationApiDescriptionModel model)
{
var moduleModel = model.GetOrAddModule(GetModuleName(apiDescription));
var controllerModel = moduleModel.GetOrAddController(GetControllerName(apiDescription));
var method = apiDescription.ActionDescriptor.GetMethodInfo();
if (controllerModel.Actions.ContainsKey(method.Name))
{
Logger.LogWarning($"Controller '{controllerModel.Name}' contains more than one action with name '{method.Name}' for module '{moduleModel.Name}'. Ignored: " + apiDescription.ActionDescriptor.GetMethodInfo());
return;
}
var returnValue = new ReturnValueApiDescriptionModel(method.ReturnType);
var actionModel = controllerModel.AddAction(new ActionApiDescriptionModel(
method.Name,
returnValue,
apiDescription.RelativePath,
apiDescription.HttpMethod
));
AddParameterDescriptionsToModel(actionModel, method, apiDescription);
}
private static string GetControllerName(ApiDescription apiDescription)
{
return apiDescription.GroupName?.RemovePostFix(ApplicationService.CommonPostfixes)
?? apiDescription.ActionDescriptor.AsControllerActionDescriptor().ControllerName;
}
private void AddParameterDescriptionsToModel(ActionApiDescriptionModel actionModel, MethodInfo method, ApiDescription apiDescription)
{
if (!apiDescription.ParameterDescriptions.Any())
{
return;
}
var matchedMethodParamNames = ArrayMatcher.Match(
apiDescription.ParameterDescriptions.Select(p => p.Name).ToArray(),
method.GetParameters().Select(GetMethodParamName).ToArray()
);
for (var i = 0; i < apiDescription.ParameterDescriptions.Count; i++)
{
var parameterDescription = apiDescription.ParameterDescriptions[i];
var matchedMethodParamName = matchedMethodParamNames.Length > i
? matchedMethodParamNames[i]
: parameterDescription.Name;
actionModel.AddParameter(new ParameterApiDescriptionModel(
parameterDescription.Name,
matchedMethodParamName,
parameterDescription.Type,
parameterDescription.RouteInfo?.IsOptional ?? false,
parameterDescription.RouteInfo?.DefaultValue,
parameterDescription.RouteInfo?.Constraints?.Select(c => c.GetType().Name).ToArray(),
parameterDescription.Source.Id
)
);
}
}
public string GetMethodParamName(ParameterInfo parameterInfo)
{
var modelNameProvider = parameterInfo.GetCustomAttributes()
.OfType<IModelNameProvider>()
.FirstOrDefault();
if (modelNameProvider == null)
{
return parameterInfo.Name;
}
return modelNameProvider.Name;
}
private string GetModuleName(ApiDescription apiDescription)
{
var controllerType = apiDescription.ActionDescriptor.AsControllerActionDescriptor().ControllerTypeInfo.AsType();
if (controllerType == null)
{
return AbpControllerAssemblySetting.DefaultServiceModuleName;
}
foreach (var controllerSetting in _options.AppServiceControllers.ControllerAssemblySettings)
{
if (Equals(controllerType.Assembly, controllerSetting.Assembly))
{
return controllerSetting.ModuleName;
}
}
return AbpControllerAssemblySetting.DefaultServiceModuleName;
}
}
}

2
src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ControllerAssemblySettingList.cs

@ -10,7 +10,7 @@ namespace Volo.Abp.AspNetCore.Mvc
[CanBeNull] [CanBeNull]
public AbpControllerAssemblySetting GetSettingOrNull(Type controllerType) public AbpControllerAssemblySetting GetSettingOrNull(Type controllerType)
{ {
return this.FirstOrDefault(controllerSetting => controllerSetting.Assembly == controllerType.GetAssembly()); return this.FirstOrDefault(controllerSetting => controllerSetting.Assembly == controllerType.Assembly);
} }
} }
} }

42
src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Utils/ArrayMatcher.cs

@ -0,0 +1,42 @@
using System.Collections.Generic;
namespace Volo.Abp.AspNetCore.Mvc.Utils
{
internal static class ArrayMatcher
{
public static T[] Match<T>(T[] sourceArray, T[] destinationArray)
{
var result = new List<T>();
var currentMethodParamIndex = 0;
var parentItem = default(T);
foreach (var sourceItem in sourceArray)
{
if (currentMethodParamIndex < destinationArray.Length)
{
var destinationItem = destinationArray[currentMethodParamIndex];
if (EqualityComparer<T>.Default.Equals(sourceItem, destinationItem))
{
parentItem = default(T);
currentMethodParamIndex++;
}
else
{
if (parentItem == null)
{
parentItem = destinationItem;
currentMethodParamIndex++;
}
}
}
var resultItem = EqualityComparer<T>.Default.Equals(parentItem, default(T)) ? sourceItem : parentItem;
result.Add(resultItem);
}
return result.ToArray();
}
}
}

4
src/Volo.Abp.Http/Volo.Abp.Http.csproj

@ -16,8 +16,4 @@
<ProjectReference Include="..\Volo.Abp\Volo.Abp.csproj" /> <ProjectReference Include="..\Volo.Abp\Volo.Abp.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Volo\Abp\Http\DynamicProxying\Oas\" />
</ItemGroup>
</Project> </Project>

40
src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ActionApiDescriptionModel.cs

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
namespace Volo.Abp.Http.Modeling
{
[Serializable]
public class ActionApiDescriptionModel
{
public string Name { get; }
public string HttpMethod { get; }
public string Url { get; }
public IList<ParameterApiDescriptionModel> Parameters { get; }
public ReturnValueApiDescriptionModel ReturnValue { get; }
private ActionApiDescriptionModel()
{
}
public ActionApiDescriptionModel(string name, ReturnValueApiDescriptionModel returnValue, string url, string httpMethod = null)
{
Name = name;
ReturnValue = returnValue;
Url = url;
HttpMethod = httpMethod;
Parameters = new List<ParameterApiDescriptionModel>();
}
public ParameterApiDescriptionModel AddParameter(ParameterApiDescriptionModel parameter)
{
Parameters.Add(parameter);
return parameter;
}
}
}

47
src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ApplicationApiDescriptionModel.cs

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Volo.Abp.Http.Modeling
{
[Serializable]
public class ApplicationApiDescriptionModel
{
public IDictionary<string, ModuleApiDescriptionModel> Modules { get; }
public ApplicationApiDescriptionModel()
{
Modules = new Dictionary<string, ModuleApiDescriptionModel>();
}
public ModuleApiDescriptionModel AddModule(ModuleApiDescriptionModel module)
{
if (Modules.ContainsKey(module.Name))
{
throw new AbpException("There is already a module with same name: " + module.Name);
}
return Modules[module.Name] = module;
}
public ModuleApiDescriptionModel GetOrAddModule(string name)
{
return Modules.GetOrAdd(name, () => new ModuleApiDescriptionModel(name));
}
public ApplicationApiDescriptionModel CreateSubModel(string[] modules = null, string[] controllers = null, string[] actions = null)
{
var subModel = new ApplicationApiDescriptionModel();
foreach (var module in Modules.Values)
{
if (modules == null || modules.Contains(module.Name))
{
subModel.AddModule(module.CreateSubModel(controllers, actions));
}
}
return subModel;
}
}
}

53
src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ControllerApiDescriptionModel.cs

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Volo.Abp.Http.Modeling
{
[Serializable]
public class ControllerApiDescriptionModel
{
public string Name { get; }
public IDictionary<string, ActionApiDescriptionModel> Actions { get; }
private ControllerApiDescriptionModel()
{
}
public ControllerApiDescriptionModel(string name)
{
Name = name;
Actions = new Dictionary<string, ActionApiDescriptionModel>();
}
public ActionApiDescriptionModel AddAction(ActionApiDescriptionModel action)
{
if (Actions.ContainsKey(action.Name))
{
throw new AbpException(
$"Can not add more than one action with same name to the same controller. Controller: {Name}, Action: {action.Name}."
);
}
return Actions[action.Name] = action;
}
public ControllerApiDescriptionModel CreateSubModel(string[] actions)
{
var subModel = new ControllerApiDescriptionModel(Name);
foreach (var action in Actions.Values)
{
if (actions == null || actions.Contains(action.Name))
{
subModel.AddAction(action);
}
}
return subModel;
}
}
}

7
src/Volo.Abp.Http/Volo/Abp/Http/Modeling/IApiDescriptionModelProvider.cs

@ -0,0 +1,7 @@
namespace Volo.Abp.Http.Modeling
{
public interface IApiDescriptionModelProvider
{
ApplicationApiDescriptionModel CreateModel();
}
}

56
src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ModuleApiDescriptionModel.cs

@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Volo.Abp.Http.Modeling
{
[Serializable]
public class ModuleApiDescriptionModel
{
public string Name { get; set; }
public IDictionary<string, ControllerApiDescriptionModel> Controllers { get; }
private ModuleApiDescriptionModel()
{
}
public ModuleApiDescriptionModel(string name)
{
Name = name;
Controllers = new Dictionary<string, ControllerApiDescriptionModel>();
}
public ControllerApiDescriptionModel AddController(ControllerApiDescriptionModel controller)
{
if (Controllers.ContainsKey(controller.Name))
{
throw new AbpException($"There is already a controller with name: {controller.Name} in module: {Name}");
}
return Controllers[controller.Name] = controller;
}
public ControllerApiDescriptionModel GetOrAddController(string name)
{
return Controllers.GetOrAdd(name, () => new ControllerApiDescriptionModel(name));
}
public ModuleApiDescriptionModel CreateSubModel(string[] controllers, string[] actions)
{
var subModel = new ModuleApiDescriptionModel(Name);
foreach (var controller in Controllers.Values)
{
if (controllers == null || controllers.Contains(controller.Name))
{
subModel.AddController(controller.CreateSubModel(actions));
}
}
return subModel;
}
}
}

41
src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ParameterApiDescriptionModel.cs

@ -0,0 +1,41 @@
using System;
namespace Volo.Abp.Http.Modeling
{
[Serializable]
public class ParameterApiDescriptionModel
{
public string NameOnMethod { get; }
public string Name { get; }
public Type Type { get; }
public string TypeAsString { get; }
public bool IsOptional { get; }
public object DefaultValue { get; }
public string[] ConstraintTypes { get; }
public string BindingSourceId { get; }
private ParameterApiDescriptionModel()
{
}
public ParameterApiDescriptionModel(string name, string nameOnMethod, Type type, bool isOptional = false, object defaultValue = null, string[] constraintTypes = null, string bindingSourceId = null)
{
Name = name;
NameOnMethod = nameOnMethod;
Type = type;
TypeAsString = type.FullName;
IsOptional = isOptional;
DefaultValue = defaultValue;
ConstraintTypes = constraintTypes;
BindingSourceId = bindingSourceId;
}
}
}

17
src/Volo.Abp.Http/Volo/Abp/Http/Modeling/ReturnValueApiDescriptionModel.cs

@ -0,0 +1,17 @@
using System;
namespace Volo.Abp.Http.Modeling
{
[Serializable]
public class ReturnValueApiDescriptionModel
{
public Type Type { get; }
public string TypeAsString { get; }
public ReturnValueApiDescriptionModel(Type type)
{
Type = type;
TypeAsString = type.FullName;
}
}
}

13
src/Volo.Abp/System/AbpTypeExtensions.cs

@ -1,13 +0,0 @@
using System.Reflection;
namespace System
{
public static class AbpTypeExtensions
{
//TODO: This method can be removed because not needed anymore!
public static Assembly GetAssembly(this Type type)
{
return type.GetTypeInfo().Assembly;
}
}
}

2
src/Volo.Abp/Volo/Abp/Modularity/AbpModuleDescriptor.cs

@ -33,7 +33,7 @@ namespace Volo.Abp.Modularity
} }
Type = type; Type = type;
Assembly = type.GetAssembly(); Assembly = type.Assembly;
Instance = instance; Instance = instance;
IsLoadedAsPlugIn = isLoadedAsPlugIn; IsLoadedAsPlugIn = isLoadedAsPlugIn;

2
src/Volo.Abp/Volo/Abp/Reflection/AssemblyFinder.cs

@ -30,7 +30,7 @@ namespace Volo.Abp.Reflection
foreach (var module in _moduleContainer.Modules) foreach (var module in _moduleContainer.Modules)
{ {
assemblies.Add(module.Type.GetAssembly()); assemblies.Add(module.Type.Assembly);
} }
return assemblies.Distinct().ToImmutableList(); return assemblies.Distinct().ToImmutableList();

19
test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Utils/ArrayMacther_Tests.cs

@ -0,0 +1,19 @@
using Xunit;
namespace Volo.Abp.AspNetCore.Mvc.Utils
{
public class ArrayMacther_Tests
{
[Theory]
[InlineData(
new[] { "p1", "p2.a1", "p2.a2.b1", "p2.a2.b2", "p3", "p4", "p5.c1", "p5.c2" },
new[] { "p1", "p2", "p3", "p4", "p5" },
new[] { "p1", "p2", "p2", "p2", "p3", "p4", "p5", "p5" })
]
public void Should_Find_Correct_Items(string[] sourceArray, string[] destinationArray, string[] expectedArray)
{
var result = ArrayMatcher.Match(sourceArray, destinationArray);
Assert.Equal(expectedArray, result);
}
}
}

2
test/Volo.Abp.Tests/Volo/Abp/Reflection/AssemblyFinder_Tests.cs

@ -30,7 +30,7 @@ namespace Volo.Abp.Reflection
foreach (var moduleType in moduleTypes) foreach (var moduleType in moduleTypes)
{ {
assemblyFinder.Assemblies.ShouldContain(moduleType.GetAssembly()); assemblyFinder.Assemblies.ShouldContain(moduleType.Assembly);
} }
} }

4
test/Volo.Abp.Tests/Volo/Abp/Reflection/TypeFinder_Tests.cs

@ -18,8 +18,8 @@ namespace Volo.Abp.Reflection
var fakeAssemblyFinder = Substitute.For<IAssemblyFinder>(); var fakeAssemblyFinder = Substitute.For<IAssemblyFinder>();
fakeAssemblyFinder.Assemblies.Returns(new List<Assembly> fakeAssemblyFinder.Assemblies.Returns(new List<Assembly>
{ {
typeof(AbpKernelModule).GetAssembly(), typeof(AbpKernelModule).Assembly,
typeof(TypeFinder_Tests).GetAssembly() typeof(TypeFinder_Tests).Assembly
}); });
//Act //Act

Loading…
Cancel
Save