you can check the new project settings.
Please contact to license@abp.io to purchase a license
you can send an email to license@abp.io
I'm closing this and refunded your ticket.
The ng directory is incorrect .
you can create a new ng project with suite to check what's the path config
%UserProfile%\.abp\suite
it's generating empty components, and the URL isn't visible in the menu. How do I map the permissions for this? Or is there a different way to generate a complete UI using the ABP CLI?
you can use abp suite to generate code.
Hi, It's hard to find the problem in a few minutes.
I guess the problem is related to your DTO property types.
you can override the IApiDescriptionModelProvider
service and check the logs.
public 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;
currentMethodParamIndex++;
}
else
{
if (parentItem == null)
{
parentItem = destinationItem;
currentMethodParamIndex++;
}
}
}
var resultItem = EqualityComparer<T>.Default.Equals(parentItem, default) ? sourceItem : parentItem;
result.Add(resultItem!);
}
return result.ToArray();
}
}
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IApiDescriptionModelProvider))]
public class MyAspNetCoreApiDescriptionModelProvider : IApiDescriptionModelProvider, ITransientDependency
{
public ILogger<MyAspNetCoreApiDescriptionModelProvider> Logger { get; set; }
private readonly AspNetCoreApiDescriptionModelProviderOptions _options;
private readonly IApiDescriptionGroupCollectionProvider _descriptionProvider;
private readonly AbpAspNetCoreMvcOptions _abpAspNetCoreMvcOptions;
private readonly AbpApiDescriptionModelOptions _modelOptions;
public MyAspNetCoreApiDescriptionModelProvider(
IOptions<AspNetCoreApiDescriptionModelProviderOptions> options,
IApiDescriptionGroupCollectionProvider descriptionProvider,
IOptions<AbpAspNetCoreMvcOptions> abpAspNetCoreMvcOptions,
IOptions<AbpApiDescriptionModelOptions> modelOptions)
{
_options = options.Value;
_descriptionProvider = descriptionProvider;
_abpAspNetCoreMvcOptions = abpAspNetCoreMvcOptions.Value;
_modelOptions = modelOptions.Value;
Logger = NullLogger<MyAspNetCoreApiDescriptionModelProvider>.Instance;
}
public ApplicationApiDescriptionModel CreateApiModel(ApplicationApiDescriptionModelRequestDto input)
{
//TODO: Can cache the model?
var model = ApplicationApiDescriptionModel.Create();
foreach (var descriptionGroupItem in _descriptionProvider.ApiDescriptionGroups.Items)
{
foreach (var apiDescription in descriptionGroupItem.Items)
{
if (!apiDescription.ActionDescriptor.IsControllerAction())
{
continue;
}
AddApiDescriptionToModel(apiDescription, model, input);
}
}
foreach (var (_, module) in model.Modules)
{
var controllers = module.Controllers.GroupBy(x => x.Value.Type).ToList();
foreach (var controller in controllers.Where(x => x.Count() > 1))
{
var removedController = module.Controllers.RemoveAll(x => x.Value.IsRemoteService && controller.OrderBy(c => c.Value.ControllerGroupName).Skip(1).Contains(x));
foreach (var removed in removedController)
{
Logger.LogInformation($"The controller named '{removed.Value.Type}' was removed from ApplicationApiDescriptionModel because it same with other controller.");
}
}
}
model.NormalizeOrder();
return model;
}
private void AddApiDescriptionToModel(
ApiDescription apiDescription,
ApplicationApiDescriptionModel applicationModel,
ApplicationApiDescriptionModelRequestDto input)
{
var controllerType = apiDescription
.ActionDescriptor
.AsControllerActionDescriptor()
.ControllerTypeInfo;
var setting = FindSetting(controllerType);
var moduleModel = applicationModel.GetOrAddModule(
GetRootPath(controllerType, apiDescription.ActionDescriptor, setting),
GetRemoteServiceName(controllerType, setting)
);
var controllerModel = moduleModel.GetOrAddController(
_options.ControllerNameGenerator(controllerType, setting),
FindGroupName(controllerType) ?? apiDescription.GroupName,
apiDescription.IsRemoteService(),
apiDescription.IsIntegrationService(),
apiDescription.GetProperty<ApiVersion>()?.ToString(),
controllerType,
_modelOptions.IgnoredInterfaces
);
var method = apiDescription.ActionDescriptor.GetMethodInfo();
var uniqueMethodName = _options.ActionNameGenerator(method);
if (controllerModel.Actions.ContainsKey(uniqueMethodName))
{
Logger.LogWarning(
$"Controller '{controllerModel.ControllerName}' contains more than one action with name '{uniqueMethodName}' for module '{moduleModel.RootPath}'. Ignored: " +
method);
return;
}
Logger.LogDebug($"ActionApiDescriptionModel.Create: {controllerModel.ControllerName}.{uniqueMethodName}");
bool? allowAnonymous = null;
if (apiDescription.ActionDescriptor.EndpointMetadata.Any(x => x is IAllowAnonymous))
{
allowAnonymous = true;
}
else if (apiDescription.ActionDescriptor.EndpointMetadata.Any(x => x is IAuthorizeData))
{
allowAnonymous = false;
}
var implementFrom = controllerType.FullName;
var interfaceType = controllerType.GetInterfaces().FirstOrDefault(i => i.GetMethods().Any(x => x.ToString() == method.ToString()));
if (interfaceType != null)
{
implementFrom = TypeHelper.GetFullNameHandlingNullableAndGenerics(interfaceType);
}
var actionModel = controllerModel.AddAction(
uniqueMethodName,
ActionApiDescriptionModel.Create(
uniqueMethodName,
method,
apiDescription.RelativePath!,
apiDescription.HttpMethod,
GetSupportedVersions(controllerType, method, setting),
allowAnonymous,
implementFrom
)
);
if (input.IncludeTypes)
{
AddCustomTypesToModel(applicationModel, method);
}
AddParameterDescriptionsToModel(actionModel, method, apiDescription);
}
private static List<string> GetSupportedVersions(Type controllerType, MethodInfo method,
ConventionalControllerSetting? setting)
{
var supportedVersions = new List<ApiVersion>();
var mapToAttributes = method.GetCustomAttributes<MapToApiVersionAttribute>().ToArray();
if (mapToAttributes.Any())
{
supportedVersions.AddRange(
mapToAttributes.SelectMany(a => a.Versions)
);
}
else
{
supportedVersions.AddRange(
controllerType.GetCustomAttributes<ApiVersionAttribute>().SelectMany(a => a.Versions)
);
setting?.ApiVersions.ForEach(supportedVersions.Add);
}
return supportedVersions.Select(v => v.ToString()).Distinct().ToList();
}
private void AddCustomTypesToModel(ApplicationApiDescriptionModel applicationModel, MethodInfo method)
{
foreach (var parameterInfo in method.GetParameters())
{
try
{
AddCustomTypesToModel(applicationModel, parameterInfo.ParameterType);
}
catch(Exception e)
{
Logger.LogError($"Error while adding parameter type to model, method info: {method.Name}, parameter info: {parameterInfo.Name}, parameter type: {parameterInfo.ParameterType.FullName}");
throw e;
}
}
try
{
AddCustomTypesToModel(applicationModel, method.ReturnType);
}
catch (Exception e)
{
Logger.LogError($"Error while adding return type to model, method info: {method.Name}, return type: {method.ReturnType.FullName}");
throw e;
}
}
private static void AddCustomTypesToModel(ApplicationApiDescriptionModel applicationModel,
Type? type)
{
if (type == null)
{
return;
}
if (type.IsGenericParameter)
{
return;
}
type = AsyncHelper.UnwrapTask(type);
if (type == typeof(object) ||
type == typeof(void) ||
type == typeof(Enum) ||
type == typeof(ValueType) ||
type == typeof(DateOnly) ||
type == typeof(TimeOnly) ||
TypeHelper.IsPrimitiveExtended(type))
{
return;
}
if (TypeHelper.IsDictionary(type, out var keyType, out var valueType))
{
AddCustomTypesToModel(applicationModel, keyType);
AddCustomTypesToModel(applicationModel, valueType);
return;
}
if (TypeHelper.IsEnumerable(type, out var itemType))
{
AddCustomTypesToModel(applicationModel, itemType);
return;
}
if (type.IsGenericType && !type.IsGenericTypeDefinition)
{
var genericTypeDefinition = type.GetGenericTypeDefinition();
AddCustomTypesToModel(applicationModel, genericTypeDefinition);
foreach (var genericArgument in type.GetGenericArguments())
{
AddCustomTypesToModel(applicationModel, genericArgument);
}
return;
}
var typeName = CalculateTypeName(type);
if (applicationModel.Types.ContainsKey(typeName))
{
return;
}
applicationModel.Types[typeName] = TypeApiDescriptionModel.Create(type);
AddCustomTypesToModel(applicationModel, type.BaseType);
foreach (var propertyInfo in type.GetProperties().Where(p => p.DeclaringType == type))
{
AddCustomTypesToModel(applicationModel, propertyInfo.PropertyType);
}
}
private static string CalculateTypeName(Type type)
{
if (!type.IsGenericTypeDefinition)
{
return TypeHelper.GetFullNameHandlingNullableAndGenerics(type);
}
var i = 0;
var argumentList = type
.GetGenericArguments()
.Select(_ => "T" + i++)
.JoinAsString(",");
return $"{type.FullName!.Left(type.FullName!.IndexOf('`'))}<{argumentList}>";
}
private void AddParameterDescriptionsToModel(ActionApiDescriptionModel actionModel, MethodInfo method,
ApiDescription apiDescription)
{
if (!apiDescription.ParameterDescriptions.Any())
{
return;
}
var parameterDescriptionNames = apiDescription
.ParameterDescriptions
.Select(p => p.Name)
.ToArray();
var methodParameterNames = method
.GetParameters()
.Where(IsNotFromServicesParameter)
.Select(GetMethodParamName)
.ToArray();
var matchedMethodParamNames = ArrayMatcher.Match(
parameterDescriptionNames,
methodParameterNames
);
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(ParameterApiDescriptionModel.Create(
parameterDescription.Name,
_options.ApiParameterNameGenerator?.Invoke(parameterDescription),
matchedMethodParamName,
parameterDescription.Type,
parameterDescription.RouteInfo?.IsOptional ?? false,
parameterDescription.RouteInfo?.DefaultValue,
parameterDescription.RouteInfo?.Constraints?.Select(c => c.GetType().Name).ToArray(),
parameterDescription.Source.Id,
parameterDescription.ModelMetadata?.ContainerType != null
? parameterDescription.ParameterDescriptor?.Name ?? string.Empty
: string.Empty
)
);
}
}
private static bool IsNotFromServicesParameter(ParameterInfo parameterInfo)
{
return !parameterInfo.IsDefined(typeof(FromServicesAttribute), true);
}
public string GetMethodParamName(ParameterInfo parameterInfo)
{
var modelNameProvider = parameterInfo.GetCustomAttributes()
.OfType<IModelNameProvider>()
.FirstOrDefault();
if (modelNameProvider == null)
{
return parameterInfo.Name!;
}
return (modelNameProvider.Name ?? parameterInfo.Name)!;
}
private static string GetRootPath(
[NotNull] Type controllerType,
[NotNull] ActionDescriptor actionDescriptor,
ConventionalControllerSetting? setting)
{
if (setting != null)
{
return setting.RootPath;
}
var areaAttr = controllerType.GetCustomAttributes().OfType<AreaAttribute>().FirstOrDefault() ?? actionDescriptor.EndpointMetadata.OfType<AreaAttribute>().FirstOrDefault();
if (areaAttr != null)
{
return areaAttr.RouteValue;
}
return ModuleApiDescriptionModel.DefaultRootPath;
}
private string GetRemoteServiceName(Type controllerType, ConventionalControllerSetting? setting)
{
if (setting != null)
{
return setting.RemoteServiceName;
}
var remoteServiceAttr =
controllerType.GetCustomAttributes().OfType<RemoteServiceAttribute>().FirstOrDefault();
if (remoteServiceAttr?.Name != null)
{
return remoteServiceAttr.Name;
}
return ModuleApiDescriptionModel.DefaultRemoteServiceName;
}
private string? FindGroupName(Type controllerType)
{
var controllerNameAttribute =
controllerType.GetCustomAttributes().OfType<ControllerNameAttribute>().FirstOrDefault();
if (controllerNameAttribute?.Name != null)
{
return controllerNameAttribute.Name;
}
return null;
}
private ConventionalControllerSetting? FindSetting(Type controllerType)
{
foreach (var controllerSetting in _abpAspNetCoreMvcOptions.ConventionalControllers.ConventionalControllerSettings)
{
if (controllerSetting.GetControllerTypes().Contains(controllerType))
{
return controllerSetting;
}
}
return null;
}
}