Activities of "liangshiwei"

Hi,

The CLI gets apiname by command option, you can specify it.

https://abp.io/docs/latest/cli#options-15

yes, you can open your .abp/suite folder to update the appsettings.json

3 is Angular UI

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('`'))}&lt;{argumentList}&gt;";
    }

    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 &lt; apiDescription.ParameterDescriptions.Count; i++)
        {
            var parameterDescription = apiDescription.ParameterDescriptions[i];
            var matchedMethodParamName = matchedMethodParamNames.Length &gt; 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&lt;IModelNameProvider&gt;()
            .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&lt;AreaAttribute&gt;().FirstOrDefault() ?? actionDescriptor.EndpointMetadata.OfType&lt;AreaAttribute&gt;().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&lt;RemoteServiceAttribute&gt;().FirstOrDefault();
        if (remoteServiceAttr?.Name != null)
        {
            return remoteServiceAttr.Name;
        }

        return ModuleApiDescriptionModel.DefaultRemoteServiceName;
    }

    private string? FindGroupName(Type controllerType)
    {
        var controllerNameAttribute =
            controllerType.GetCustomAttributes().OfType&lt;ControllerNameAttribute&gt;().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;
    }
}

https://abp.io/support/questions/5427/HOW-TO-AUTHENTICATE-EXERNAL-SSO-TOKEN-WITH-ADMIN-APIS this support ticket also taking same right ?

As i see, not same.

Hi,

you should configure the angular app URL, we have an example you can check: https://github.com/abpframework/abp-samples/tree/master/DomainTenantResolver

The users authenticated through an external SSO are not our application users; the only similarity is the email address. After the user is authenticated via SSO, authorization is managed within our application.

oh, you need to configure an external OIDC provider in the authserver project

https://abp.io/docs/latest/modules/account-pro#social-external-logins

I am still confuse because as per below abp.io support ticket i have follow instruction. https://abp.io/support/questions/5427/HOW-TO-AUTHENTICATE-EXERNAL-SSO-TOKEN-WITH-ADMIN-APIS

here is talking about configure extra authentication. These are already configured by default in the abp template

You mentioned that the code I provided might not be necessary for handling SSO integration using cookies across subdomains. However, the step-by-step details are not clearly outlined. Could you provide a more detailed explanation or clarification? https://learn.microsoft.com/en-us/aspnet/core/security/cookie-sharing?view=aspnetcore-8.0

Here is abp.io how to configure SSO. You need to configure it for all applications

Hi,

could you share a minimum reproducible project with me? i will check it. : )

Hi,

you can try update bundle files.

abp bundle -t maui-blazor

Showing 641 to 650 of 5992 entries
Made with ❤️ on ABP v9.1.0-preview. Updated on November 18, 2024, 05:54