diff --git a/docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/POST.md b/docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/POST.md index 5567b42aad..e63db935a5 100644 --- a/docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/POST.md +++ b/docs/en/Community-Articles/2025-07-26-Separate-Tenant-Schema/POST.md @@ -22,7 +22,7 @@ In the shared database model, all the application data stored in a single physic ![single-shared-database](single-shared-database.png) -This is the default behavior when you [create a new ABP application](https://abp.io/docs/latest/get-started), because it is simple to begin with and proper for must applications. +This is the default behavior when you [create a new ABP application](https://abp.io/docs/latest/get-started), because it is simple to begin with and proper for most applications. In this model, a single database table may contain data of multiple tenants. Each row in these tables have a `TenantId` field which is used to distinguish the tenant data and isolate a tenant's data from other tenant users. To make your entities multi-tenant aware, all you have to do is to implement the `IMultiTenant` interface provided by the ABP Framework. diff --git a/docs/en/studio/release-notes.md b/docs/en/studio/release-notes.md index d2db328fbf..5601fe11d0 100644 --- a/docs/en/studio/release-notes.md +++ b/docs/en/studio/release-notes.md @@ -2,6 +2,33 @@ This document contains **brief release notes** for each ABP Studio release. Release notes only include **major features** and **visible enhancements**. Therefore, they don't include all the development done in the related version. +## 1.1.2 (2025-07-31) + +* Upgraded template dependencies for ABP Framework and LeptonX. (targeting ABP `9.2.3`) +* Configured LeptonX Lite logos in the templates. +* Added browser tab memory feature to remember previously selected tabs. +* Enhanced tools section with default credentials display for first-time tool usage. +* Improved module and package loading with better error handling. + +## 1.1.1 (2025-07-22) + +* Enhanced tools section with clear cookies option. +* Fixed language management module name display for imported modules. +* Improved update window messaging with "Skip this version" option. +* Fixed Docker Compose file issues in microservice template. +* Resolved RabbitMQ tool cookie problems. + +## 1.1.0 (2025-07-16) + +* Upgraded template dependencies for ABP Framework and LeptonX. (targeting ABP `9.2.2`) +* Enhanced UI scaling for all windows and improved user experience. +* Added tools section in solution runner main area with basic Grafana dashboard for microservice template. +* Improved container management during application building. +* Enhanced background task exception handling. +* Added public account module package reference to Blazor WebApp client. +* Fixed tenant database context updating errors. +* Improved optional module selection UI with better documentation integration. + ## 1.0.2 (2025-06-24) * Enhanced the ABP NuGet package installation experience. diff --git a/docs/en/studio/version-mapping.md b/docs/en/studio/version-mapping.md index 51e0de5bbd..6fe3ba95be 100644 --- a/docs/en/studio/version-mapping.md +++ b/docs/en/studio/version-mapping.md @@ -4,6 +4,8 @@ This document provides a general overview of the relationship between various ve | **ABP Studio Version** | **ABP Version of Startup Template** | |------------------------|---------------------------| +| 1.1.2 | 9.2.3 | +| 1.1.0 - 1.1.1 | 9.2.2 | | 1.0.2 | 9.2.1 | | 1.0.1 | 9.2.0 | | 1.0.0 | 9.2.0 | diff --git a/docs/en/tutorials/modular-crm/part-05.md b/docs/en/tutorials/modular-crm/part-05.md index 139c4a9a8b..d2082667aa 100644 --- a/docs/en/tutorials/modular-crm/part-05.md +++ b/docs/en/tutorials/modular-crm/part-05.md @@ -167,7 +167,7 @@ The `ReplaceDbContext` attribute allows the use of the `ModularCrmDbContext` cla ````csharp public class ModularCrmDbContext : AbpDbContext, - IProductsDbContext, + ICatalogDbContext, IOrderingDbContext //NEW: IMPLEMENT THE INTERFACE { public DbSet Products { get; set; } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Resources/WebRequestResources.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Resources/WebRequestResources.cs index e68b671fc6..4e346b1274 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Resources/WebRequestResources.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Resources/WebRequestResources.cs @@ -30,6 +30,8 @@ public class WebRequestResources : IWebRequestResources, IScopedDependency } Resources.Add(path, resources); - return resources; + + // Create a new list as return value to avoid any changes to the list will affect the value in Resources dictionary. + return resources.ToList(); } } diff --git a/framework/src/Volo.Abp.Mapperly/Volo/Abp/Mapperly/MapperlyAutoObjectMappingProvider.cs b/framework/src/Volo.Abp.Mapperly/Volo/Abp/Mapperly/MapperlyAutoObjectMappingProvider.cs index 7685aa03ac..677649b79a 100644 --- a/framework/src/Volo.Abp.Mapperly/Volo/Abp/Mapperly/MapperlyAutoObjectMappingProvider.cs +++ b/framework/src/Volo.Abp.Mapperly/Volo/Abp/Mapperly/MapperlyAutoObjectMappingProvider.cs @@ -1,5 +1,9 @@ using System; +using System.Collections; +using System.Collections.Concurrent; using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; using System.Reflection; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Data; @@ -19,6 +23,8 @@ public class MapperlyAutoObjectMappingProvider : MapperlyAutoObjectMap public class MapperlyAutoObjectMappingProvider : IAutoObjectMappingProvider { + protected static readonly ConcurrentDictionary> MapCache = new(); + protected IServiceProvider ServiceProvider { get; } public MapperlyAutoObjectMappingProvider(IServiceProvider serviceProvider) @@ -28,6 +34,11 @@ public class MapperlyAutoObjectMappingProvider : IAutoObjectMappingProvider public virtual TDestination Map(object source) { + if (TryToMapCollection((TSource)source, default, out var collectionResult)) + { + return collectionResult; + } + var mapper = ServiceProvider.GetService>(); if (mapper != null) { @@ -54,6 +65,11 @@ public class MapperlyAutoObjectMappingProvider : IAutoObjectMappingProvider public virtual TDestination Map(TSource source, TDestination destination) { + if (TryToMapCollection(source, destination, out var collectionResult)) + { + return collectionResult; + } + var mapper = ServiceProvider.GetService>(); if (mapper != null) { @@ -80,6 +96,126 @@ public class MapperlyAutoObjectMappingProvider : IAutoObjectMappingProvider $" {TypeHelper.GetFullNameHandlingNullableAndGenerics(typeof(IAbpReverseMapperlyMapper))} was found"); } + protected virtual bool TryToMapCollection(TSource source, TDestination? destination, out TDestination collectionResult) + { + if (!ObjectMappingHelper.IsCollectionGenericType(out var sourceArgumentType, out var destinationArgumentType, out var definitionGenericType)) + { + collectionResult = default!; + return false; + } + + var mapperType = typeof(IAbpMapperlyMapper<,>).MakeGenericType(sourceArgumentType, destinationArgumentType); + var mapper = ServiceProvider.GetService(mapperType); + if (mapper == null) + { + mapperType = typeof(IAbpReverseMapperlyMapper<,>).MakeGenericType(destinationArgumentType, sourceArgumentType); + mapper = ServiceProvider.GetService(mapperType); + if (mapper == null) + { + //skip, no specific mapper + collectionResult = default!; + return false; + } + } + + var invoker = MapCache.GetOrAdd( + $"{mapperType.FullName}_{(destination == null ? "MapMethodWithSingleParameter" : "MapMethodWithDoubleParameters")}", + _ => CreateMapDelegate(mapperType, sourceArgumentType, destinationArgumentType, destination != null)); + + var sourceList = source!.As(); + var result = definitionGenericType.IsGenericType + ? Activator.CreateInstance(definitionGenericType.MakeGenericType(destinationArgumentType))!.As() + : Array.CreateInstance(destinationArgumentType, sourceList.Count); + + if (destination != null && !destination.GetType().IsArray) + { + //Clear destination collection if destination not an array, We won't change array just same behavior as AutoMapper. + destination.As().Clear(); + } + + for (var i = 0; i < sourceList.Count; i++) + { + var invokeResult = destination == null + ? invoker(this, sourceList[i]!, null!) + : invoker(this, sourceList[i]!, Activator.CreateInstance(destinationArgumentType)!); + + if (definitionGenericType.IsGenericType) + { + result.Add(invokeResult); + destination?.As().Add(invokeResult); + } + else + { + result[i] = invokeResult; + } + } + + if (destination != null && destination.GetType().IsArray) + { + //Return the new collection if destination is an array, We won't change array just same behavior as AutoMapper. + collectionResult = (TDestination)result; + return true; + } + + //Return the destination if destination exists. The parameter reference equals with return object. + collectionResult = destination ?? (TDestination)result; + return true; + } + + protected virtual Func CreateMapDelegate( + Type mapperType, + Type sourceArgumentType, + Type destinationArgumentType, + bool hasDestination) + { + var methods = typeof(MapperlyAutoObjectMappingProvider) + .GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) + .Where(x => x.Name == nameof(Map)) + .Where(x => + { + var parameters = x.GetParameters(); + return (hasDestination || parameters.Length == 1) && + (!hasDestination || parameters.Length == 2); + }) + .ToList(); + + if (methods.Count == 0) + { + throw new AbpException($"Could not find a method named '{nameof(Map)}'" + + $" with parameters({(hasDestination ? sourceArgumentType + ", " + destinationArgumentType : sourceArgumentType.ToString())})" + + $" in the type '{mapperType}'."); + } + + if (methods.Count > 1) + { + throw new AbpException($"Found more than one method named '{nameof(Map)}'" + + $" with parameters({(hasDestination ? sourceArgumentType + ", " + destinationArgumentType : sourceArgumentType.ToString())})" + + $" in the type '{mapperType}'."); + } + + var method = methods[0].MakeGenericMethod(sourceArgumentType, destinationArgumentType); + + var instanceParam = Expression.Parameter(typeof(object), "mapper"); + var sourceParam = Expression.Parameter(typeof(object), "source"); + var destinationParam = Expression.Parameter(typeof(object), "destination"); + + var instanceCast = Expression.Convert(instanceParam, method.DeclaringType!); + var callParams = new List + { + Expression.Convert(sourceParam, sourceArgumentType) + }; + + if (hasDestination) + { + callParams.Add(Expression.Convert(destinationParam, destinationArgumentType)); + } + + var call = Expression.Call(instanceCast, method, callParams); + var callConvert = Expression.Convert(call, typeof(object)); + + return Expression.Lambda>(callConvert, instanceParam, sourceParam, destinationParam).Compile(); + } + protected virtual ExtraPropertyDictionary GetExtraProperties(TDestination destination) { var extraProperties = new ExtraPropertyDictionary(); diff --git a/framework/src/Volo.Abp.ObjectMapping/Volo/Abp/ObjectMapping/DefaultObjectMapper.cs b/framework/src/Volo.Abp.ObjectMapping/Volo/Abp/ObjectMapping/DefaultObjectMapper.cs index 30733b1e24..faf8c4e258 100644 --- a/framework/src/Volo.Abp.ObjectMapping/Volo/Abp/ObjectMapping/DefaultObjectMapper.cs +++ b/framework/src/Volo.Abp.ObjectMapping/Volo/Abp/ObjectMapping/DefaultObjectMapper.cs @@ -5,6 +5,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using System.Linq.Expressions; using System.Reflection; using Volo.Abp.DependencyInjection; @@ -25,7 +26,7 @@ public class DefaultObjectMapper : DefaultObjectMapper, IObjectMapper< public class DefaultObjectMapper : IObjectMapper, ITransientDependency { - protected static ConcurrentDictionary MethodInfoCache { get; } = new ConcurrentDictionary(); + protected static readonly ConcurrentDictionary> MapCache = new(); public IAutoObjectMappingProvider AutoObjectMappingProvider { get; } protected IServiceProvider ServiceProvider { get; } @@ -122,7 +123,7 @@ public class DefaultObjectMapper : IObjectMapper, ITransientDependency protected virtual bool TryToMapCollection(IServiceScope serviceScope, TSource source, TDestination? destination, out TDestination collectionResult) { - if (!IsCollectionGenericType(out var sourceArgumentType, out var destinationArgumentType, out var definitionGenericType)) + if (!ObjectMappingHelper.IsCollectionGenericType(out var sourceArgumentType, out var destinationArgumentType, out var definitionGenericType)) { collectionResult = default!; return false; @@ -137,46 +138,9 @@ public class DefaultObjectMapper : IObjectMapper, ITransientDependency return false; } - var cacheKey = $"{mapperType.FullName}_{(destination == null ? "MapMethodWithSingleParameter" : "MapMethodWithDoubleParameters")}"; - var method = MethodInfoCache.GetOrAdd( - cacheKey, - _ => - { - var methods = specificMapper - .GetType() - .GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) - .Where(x => x.Name == nameof(IObjectMapper.Map)) - .Where(x => - { - var parameters = x.GetParameters(); - if (destination == null && parameters.Length != 1 || - destination != null && parameters.Length != 2 || - parameters[0].ParameterType != sourceArgumentType) - { - return false; - } - - return destination == null || parameters[1].ParameterType == destinationArgumentType; - }) - .ToList(); - - if (methods.IsNullOrEmpty()) - { - throw new AbpException($"Could not find a method named '{nameof(IObjectMapper.Map)}'" + - $" with parameters({(destination == null ? sourceArgumentType.ToString() : sourceArgumentType + "," + destinationArgumentType)})" + - $" in the type '{mapperType}'."); - } - - if (methods.Count > 1) - { - throw new AbpException($"Found more than one method named '{nameof(IObjectMapper.Map)}'" + - $" with parameters({(destination == null ? sourceArgumentType.ToString() : sourceArgumentType + "," + destinationArgumentType)})" + - $" in the type '{mapperType}'."); - } - - return methods.First(); - } - ); + var invoker = MapCache.GetOrAdd( + $"{mapperType.FullName}_{(destination == null ? "MapMethodWithSingleParameter" : "MapMethodWithDoubleParameters")}", + _ => CreateMapDelegate(mapperType, sourceArgumentType, destinationArgumentType, destination != null)); var sourceList = source!.As(); var result = definitionGenericType.IsGenericType @@ -192,8 +156,8 @@ public class DefaultObjectMapper : IObjectMapper, ITransientDependency for (var i = 0; i < sourceList.Count; i++) { var invokeResult = destination == null - ? method.Invoke(specificMapper, new [] { sourceList[i] })! - : method.Invoke(specificMapper, new [] { sourceList[i], Activator.CreateInstance(destinationArgumentType)! })!; + ? invoker(specificMapper, sourceList[i]!, null!) + : invoker(specificMapper, sourceList[i]!, Activator.CreateInstance(destinationArgumentType)!); if (definitionGenericType.IsGenericType) { @@ -218,61 +182,64 @@ public class DefaultObjectMapper : IObjectMapper, ITransientDependency return true; } - protected virtual bool IsCollectionGenericType(out Type sourceArgumentType, out Type destinationArgumentType, out Type definitionGenericType) + protected virtual Func CreateMapDelegate( + Type mapperType, + Type sourceArgumentType, + Type destinationArgumentType, + bool hasDestination) { - sourceArgumentType = default!; - destinationArgumentType = default!; - definitionGenericType = default!; - - if ((!typeof(TSource).IsGenericType && !typeof(TSource).IsArray) || - (!typeof(TDestination).IsGenericType && !typeof(TDestination).IsArray)) - { - return false; - } + var methods = mapperType + .GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) + .Where(x => x.Name == nameof(IObjectMapper.Map)) + .Where(x => + { + var parameters = x.GetParameters(); + if (!hasDestination && parameters.Length != 1 || + hasDestination && parameters.Length != 2 || + parameters[0].ParameterType != sourceArgumentType) + { + return false; + } - var supportedCollectionTypes = new[] - { - typeof(IEnumerable<>), - typeof(ICollection<>), - typeof(Collection<>), - typeof(IList<>), - typeof(List<>) - }; + return !hasDestination || parameters[1].ParameterType == destinationArgumentType; + }) + .ToList(); - if (typeof(TSource).IsGenericType && supportedCollectionTypes.Any(x => x == typeof(TSource).GetGenericTypeDefinition())) + if (methods.Count == 0) { - sourceArgumentType = typeof(TSource).GenericTypeArguments[0]; + throw new AbpException($"Could not find a method named '{nameof(IObjectMapper.Map)}'" + + $" with parameters({(hasDestination ? sourceArgumentType + ", " + destinationArgumentType : sourceArgumentType.ToString())})" + + $" in the type '{mapperType}'."); } - if (typeof(TSource).IsArray) + if (methods.Count > 1) { - sourceArgumentType = typeof(TSource).GetElementType()!; + throw new AbpException($"Found more than one method named '{nameof(IObjectMapper.Map)}'" + + $" with parameters({(hasDestination ? sourceArgumentType + ", " + destinationArgumentType : sourceArgumentType.ToString())})" + + $" in the type '{mapperType}'."); } - if (sourceArgumentType == default!) - { - return false; - } + var method = methods[0]; - definitionGenericType = typeof(List<>); - if (typeof(TDestination).IsGenericType && supportedCollectionTypes.Any(x => x == typeof(TDestination).GetGenericTypeDefinition())) - { - destinationArgumentType = typeof(TDestination).GenericTypeArguments[0]; + var instanceParam = Expression.Parameter(typeof(object), "mapper"); + var sourceParam = Expression.Parameter(typeof(object), "source"); + var destinationParam = Expression.Parameter(typeof(object), "destination"); - if (typeof(TDestination).GetGenericTypeDefinition() == typeof(ICollection<>) || - typeof(TDestination).GetGenericTypeDefinition() == typeof(Collection<>)) - { - definitionGenericType = typeof(Collection<>); - } - } + var instanceCast = Expression.Convert(instanceParam, method.DeclaringType!); + var callParams = new List + { + Expression.Convert(sourceParam, sourceArgumentType) + }; - if (typeof(TDestination).IsArray) + if (hasDestination) { - destinationArgumentType = typeof(TDestination).GetElementType()!; - definitionGenericType = typeof(Array); + callParams.Add(Expression.Convert(destinationParam, destinationArgumentType)); } - return destinationArgumentType != default!; + var call = Expression.Call(instanceCast, method, callParams); + var callConvert = Expression.Convert(call, typeof(object)); + + return Expression.Lambda>(callConvert, instanceParam, sourceParam, destinationParam).Compile(); } protected virtual TDestination AutoMap(object source) diff --git a/framework/src/Volo.Abp.ObjectMapping/Volo/Abp/ObjectMapping/ObjectMappingHelper.cs b/framework/src/Volo.Abp.ObjectMapping/Volo/Abp/ObjectMapping/ObjectMappingHelper.cs new file mode 100644 index 0000000000..5949edaba9 --- /dev/null +++ b/framework/src/Volo.Abp.ObjectMapping/Volo/Abp/ObjectMapping/ObjectMappingHelper.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; + +namespace Volo.Abp.ObjectMapping; + +public static class ObjectMappingHelper +{ + private static readonly ConcurrentDictionary<(Type, Type), (Type sourceArgumentType, Type destinationArgumentType, Type definitionGenericType)?> Cache = new(); + + public static bool IsCollectionGenericType( + out Type sourceArgumentType, + out Type destinationArgumentType, + out Type definitionGenericType) + { + var cached = Cache.GetOrAdd((typeof(TSource), typeof(TDestination)), _ => IsCollectionGenericTypeInternal()); + if (cached == null) + { + sourceArgumentType = destinationArgumentType = definitionGenericType = null!; + return false; + } + + (sourceArgumentType, destinationArgumentType, definitionGenericType) = cached.Value; + return true; + } + + private static (Type, Type, Type)? IsCollectionGenericTypeInternal() + { + if (!IsCollectionGenericTypeInternal(typeof(TSource), out var sourceArgumentType, out _) || + !IsCollectionGenericTypeInternal(typeof(TDestination), out var destinationArgumentType, out var definitionGenericType)) + { + return null; + } + + return (sourceArgumentType, destinationArgumentType, definitionGenericType); + } + + private static bool IsCollectionGenericTypeInternal(Type type, out Type elementType, out Type definitionGenericType) + { + var supportedCollectionTypes = new[] + { + typeof(IEnumerable<>), + typeof(ICollection<>), + typeof(Collection<>), + typeof(IList<>), + typeof(List<>) + }; + + if (type.IsArray) + { + elementType = type.GetElementType()!; + definitionGenericType = type; + return true; + } + + if (type.IsGenericType && + supportedCollectionTypes.Contains(type.GetGenericTypeDefinition()) || + type.GetInterfaces().Any(i => i.IsGenericType && supportedCollectionTypes.Contains(i.GetGenericTypeDefinition()))) + { + elementType = type.GetGenericArguments()[0]; + definitionGenericType = type.GetGenericTypeDefinition(); + if (definitionGenericType == typeof(IEnumerable<>) || + definitionGenericType == typeof(IList<>)) + { + definitionGenericType = typeof(List<>); + } + + if (definitionGenericType == typeof(ICollection<>)) + { + definitionGenericType = typeof(Collection<>); + } + return true; + } + + elementType = null!; + definitionGenericType = null!; + return false; + } +} diff --git a/framework/test/Volo.Abp.Mapperly.Tests/Volo/Abp/Mapperly/AbpMapperlyModule_Basic_Tests.cs b/framework/test/Volo.Abp.Mapperly.Tests/Volo/Abp/Mapperly/AbpMapperlyModule_Basic_Tests.cs index 89861f6947..3f0773f0a1 100644 --- a/framework/test/Volo.Abp.Mapperly.Tests/Volo/Abp/Mapperly/AbpMapperlyModule_Basic_Tests.cs +++ b/framework/test/Volo.Abp.Mapperly.Tests/Volo/Abp/Mapperly/AbpMapperlyModule_Basic_Tests.cs @@ -1,4 +1,7 @@ using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices.JavaScript; using Microsoft.Extensions.DependencyInjection; using Shouldly; using Volo.Abp.Mapperly.SampleClasses; @@ -41,6 +44,57 @@ public class AbpMapperlyModule_Basic_Tests : AbpIntegratedTest, List>(new List + { + new MyEntity { Number = 42 }, + new MyEntity { Number = 43 } + }); + + dto.Count.ShouldBe(2); + dto[0].Number.ShouldBe(42); + dto[1].Number.ShouldBe(43); + + var dto2 = _objectMapper.Map, MyEntityDto[]>(new List + { + new MyEntity { Number = 42 }, + new MyEntity { Number = 43 } + }.AsReadOnly()); + + dto2.Length.ShouldBe(2); + dto2[0].Number.ShouldBe(42); + dto2[1].Number.ShouldBe(43); + + var dtoList = new List(); + { + new MyEntityDto() { Number = 44 }; + new MyEntityDto() { Number = 45 }; + } + + _objectMapper.Map, List>(new List + { + new MyEntity { Number = 42 }, + new MyEntity { Number = 43 } + }, dtoList); + + dtoList.Count.ShouldBe(2); + dtoList[0].Number.ShouldBe(42); + dtoList[1].Number.ShouldBe(43); + + var dtoArray = dtoList.ToArray(); + _objectMapper.Map, MyEntityDto[]>(new List + { + new MyEntity { Number = 42 }, + new MyEntity { Number = 43 } + }.AsReadOnly(), dtoArray); + + dtoArray.Length.ShouldBe(2); + dtoArray[0].Number.ShouldBe(42); + dtoArray[1].Number.ShouldBe(43); + } + [Fact] public void Should_Map_Enum() { diff --git a/modules/account/src/Volo.Abp.Account.Application/Volo.Abp.Account.Application.csproj b/modules/account/src/Volo.Abp.Account.Application/Volo.Abp.Account.Application.csproj index d8c149f8b5..b4530a5dd7 100644 --- a/modules/account/src/Volo.Abp.Account.Application/Volo.Abp.Account.Application.csproj +++ b/modules/account/src/Volo.Abp.Account.Application/Volo.Abp.Account.Application.csproj @@ -21,6 +21,7 @@ + diff --git a/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/AbpAccountApplicationMappers.cs b/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/AbpAccountApplicationMappers.cs new file mode 100644 index 0000000000..dcfaf275dd --- /dev/null +++ b/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/AbpAccountApplicationMappers.cs @@ -0,0 +1,21 @@ +using Riok.Mapperly.Abstractions; +using Volo.Abp.Identity; +using Volo.Abp.Mapperly; + +namespace Volo.Abp.Account; + +[Mapper] +[MapExtraProperties] +public partial class IdentityUserToProfileDtoMapper : MapperBase +{ + [MapperIgnoreTarget(nameof(ProfileDto.HasPassword))] + public override partial ProfileDto Map(IdentityUser source); + + [MapperIgnoreTarget(nameof(ProfileDto.HasPassword))] + public override partial void Map(IdentityUser source, ProfileDto destination); + + public override void AfterMap(IdentityUser source, ProfileDto destination) + { + destination.HasPassword = source.PasswordHash != null; + } +} \ No newline at end of file diff --git a/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/AbpAccountApplicationModule.cs b/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/AbpAccountApplicationModule.cs index 283e06a934..04f25d33d8 100644 --- a/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/AbpAccountApplicationModule.cs +++ b/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/AbpAccountApplicationModule.cs @@ -1,4 +1,4 @@ -using Volo.Abp.AutoMapper; +using Volo.Abp.Mapperly; using Volo.Abp.Emailing; using Volo.Abp.Identity; using Volo.Abp.Modularity; @@ -12,7 +12,8 @@ namespace Volo.Abp.Account; typeof(AbpAccountApplicationContractsModule), typeof(AbpIdentityApplicationModule), typeof(AbpUiNavigationModule), - typeof(AbpEmailingModule) + typeof(AbpEmailingModule), + typeof(AbpMapperlyModule) )] public class AbpAccountApplicationModule : AbpModule { @@ -23,11 +24,6 @@ public class AbpAccountApplicationModule : AbpModule options.FileSets.AddEmbedded(); }); - Configure(options => - { - options.AddProfile(validate: true); - }); - Configure(options => { options.Applications["MVC"].Urls[AccountUrlNames.PasswordReset] = "Account/ResetPassword"; diff --git a/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/AbpAccountApplicationModuleAutoMapperProfile.cs b/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/AbpAccountApplicationModuleAutoMapperProfile.cs deleted file mode 100644 index 0c117d98d0..0000000000 --- a/modules/account/src/Volo.Abp.Account.Application/Volo/Abp/Account/AbpAccountApplicationModuleAutoMapperProfile.cs +++ /dev/null @@ -1,15 +0,0 @@ -using AutoMapper; -using Volo.Abp.Identity; - -namespace Volo.Abp.Account; - -public class AbpAccountApplicationModuleAutoMapperProfile : Profile -{ - public AbpAccountApplicationModuleAutoMapperProfile() - { - CreateMap() - .ForMember(dest => dest.HasPassword, - op => op.MapFrom(src => src.PasswordHash != null)) - .MapExtraProperties(); - } -} diff --git a/modules/account/src/Volo.Abp.Account.Blazor/AbpAccountBlazorAutoMapperProfile.cs b/modules/account/src/Volo.Abp.Account.Blazor/AbpAccountBlazorAutoMapperProfile.cs deleted file mode 100644 index af59d75321..0000000000 --- a/modules/account/src/Volo.Abp.Account.Blazor/AbpAccountBlazorAutoMapperProfile.cs +++ /dev/null @@ -1,20 +0,0 @@ -using AutoMapper; -using Volo.Abp.Account.Blazor.Pages.Account; -using Volo.Abp.AutoMapper; -using Volo.Abp.Identity; - -namespace Volo.Abp.Account.Blazor; - -public class AbpAccountBlazorAutoMapperProfile : Profile -{ - public AbpAccountBlazorAutoMapperProfile() - { - CreateMap() - .MapExtraProperties() - .Ignore(x => x.PhoneNumberConfirmed) - .Ignore(x => x.EmailConfirmed); - - CreateMap() - .MapExtraProperties(); - } -} diff --git a/modules/account/src/Volo.Abp.Account.Blazor/AbpAccountBlazorMappers.cs b/modules/account/src/Volo.Abp.Account.Blazor/AbpAccountBlazorMappers.cs new file mode 100644 index 0000000000..98ef9f9ab6 --- /dev/null +++ b/modules/account/src/Volo.Abp.Account.Blazor/AbpAccountBlazorMappers.cs @@ -0,0 +1,27 @@ +using Riok.Mapperly.Abstractions; +using Volo.Abp.Account.Blazor.Pages.Account; +using Volo.Abp.Mapperly; +using Volo.Abp.Identity; + +namespace Volo.Abp.Account.Blazor; + +[Mapper] +[MapExtraProperties] +public partial class ProfileDtoToPersonalInfoModelMapper : MapperBase +{ + [MapperIgnoreTarget(nameof(PersonalInfoModel.PhoneNumberConfirmed))] + [MapperIgnoreTarget(nameof(PersonalInfoModel.EmailConfirmed))] + public override partial PersonalInfoModel Map(ProfileDto source); + + [MapperIgnoreTarget(nameof(PersonalInfoModel.PhoneNumberConfirmed))] + [MapperIgnoreTarget(nameof(PersonalInfoModel.EmailConfirmed))] + public override partial void Map(ProfileDto source, PersonalInfoModel destination); +} + +[Mapper] +[MapExtraProperties] +public partial class PersonalInfoModelToUpdateProfileDtoMapper : MapperBase +{ + public override partial UpdateProfileDto Map(PersonalInfoModel source); + public override partial void Map(PersonalInfoModel source, UpdateProfileDto destination); +} \ No newline at end of file diff --git a/modules/account/src/Volo.Abp.Account.Blazor/AbpAccountBlazorModule.cs b/modules/account/src/Volo.Abp.Account.Blazor/AbpAccountBlazorModule.cs index 61b42e1807..6a44f156ee 100644 --- a/modules/account/src/Volo.Abp.Account.Blazor/AbpAccountBlazorModule.cs +++ b/modules/account/src/Volo.Abp.Account.Blazor/AbpAccountBlazorModule.cs @@ -2,7 +2,7 @@ using Volo.Abp.Account.Blazor.Pages.Account; using Volo.Abp.AspNetCore.Components.Web.Theming; using Volo.Abp.AspNetCore.Components.Web.Theming.Routing; -using Volo.Abp.AutoMapper; +using Volo.Abp.Mapperly; using Volo.Abp.Modularity; using Volo.Abp.ObjectExtending; using Volo.Abp.ObjectExtending.Modularity; @@ -13,21 +13,16 @@ namespace Volo.Abp.Account.Blazor; [DependsOn( typeof(AbpAspNetCoreComponentsWebThemingModule), - typeof(AbpAutoMapperModule), + typeof(AbpMapperlyModule), typeof(AbpAccountApplicationContractsModule) )] public class AbpAccountBlazorModule : AbpModule { private readonly static OneTimeRunner OneTimeRunner = new OneTimeRunner(); - + public override void ConfigureServices(ServiceConfigurationContext context) { - context.Services.AddAutoMapperObjectMapper(); - - Configure(options => - { - options.AddProfile(validate: true); - }); + context.Services.AddMapperlyObjectMapper(); Configure(options => { @@ -39,7 +34,7 @@ public class AbpAccountBlazorModule : AbpModule options.AdditionalAssemblies.Add(typeof(AbpAccountBlazorModule).Assembly); }); } - + public override void PostConfigureServices(ServiceConfigurationContext context) { OneTimeRunner.Run(() => diff --git a/modules/account/src/Volo.Abp.Account.Blazor/Volo.Abp.Account.Blazor.csproj b/modules/account/src/Volo.Abp.Account.Blazor/Volo.Abp.Account.Blazor.csproj index 0eaa3cd978..7590216d06 100644 --- a/modules/account/src/Volo.Abp.Account.Blazor/Volo.Abp.Account.Blazor.csproj +++ b/modules/account/src/Volo.Abp.Account.Blazor/Volo.Abp.Account.Blazor.csproj @@ -10,7 +10,7 @@ - + diff --git a/modules/account/src/Volo.Abp.Account.Web/AbpAccountWebAutomapperProfile.cs b/modules/account/src/Volo.Abp.Account.Web/AbpAccountWebAutomapperProfile.cs deleted file mode 100644 index cdc92e8b19..0000000000 --- a/modules/account/src/Volo.Abp.Account.Web/AbpAccountWebAutomapperProfile.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Volo.Abp.Account.Web.Pages.Account; -using Volo.Abp.Identity; -using AutoMapper; -using Volo.Abp.Account.Web.Pages.Account.Components.ProfileManagementGroup.PersonalInfo; - -namespace Volo.Abp.Account.Web; - -public class AbpAccountWebAutoMapperProfile : Profile -{ - public AbpAccountWebAutoMapperProfile() - { - CreateMap() - .MapExtraProperties(); - } -} diff --git a/modules/account/src/Volo.Abp.Account.Web/AbpAccountWebMappers.cs b/modules/account/src/Volo.Abp.Account.Web/AbpAccountWebMappers.cs new file mode 100644 index 0000000000..09a2e8373c --- /dev/null +++ b/modules/account/src/Volo.Abp.Account.Web/AbpAccountWebMappers.cs @@ -0,0 +1,13 @@ +using Riok.Mapperly.Abstractions; +using Volo.Abp.Account.Web.Pages.Account.Components.ProfileManagementGroup.PersonalInfo; +using Volo.Abp.Mapperly; + +namespace Volo.Abp.Account.Web; + +[Mapper] +[MapExtraProperties] +public partial class ProfileDtoToPersonalInfoModelMapper : MapperBase +{ + public override partial AccountProfilePersonalInfoManagementGroupViewComponent.PersonalInfoModel Map(ProfileDto source); + public override partial void Map(ProfileDto source, AccountProfilePersonalInfoManagementGroupViewComponent.PersonalInfoModel destination); +} \ No newline at end of file diff --git a/modules/account/src/Volo.Abp.Account.Web/AbpAccountWebModule.cs b/modules/account/src/Volo.Abp.Account.Web/AbpAccountWebModule.cs index 1b41deaff7..f246beed84 100644 --- a/modules/account/src/Volo.Abp.Account.Web/AbpAccountWebModule.cs +++ b/modules/account/src/Volo.Abp.Account.Web/AbpAccountWebModule.cs @@ -8,7 +8,7 @@ using Volo.Abp.AspNetCore.Mvc.Localization; using Volo.Abp.AspNetCore.Mvc.UI.Bundling; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Toolbars; -using Volo.Abp.AutoMapper; +using Volo.Abp.Mapperly; using Volo.Abp.ExceptionHandling; using Volo.Abp.Http.ProxyScripting.Generators.JQuery; using Volo.Abp.Identity.AspNetCore; @@ -24,14 +24,14 @@ namespace Volo.Abp.Account.Web; [DependsOn( typeof(AbpAccountApplicationContractsModule), typeof(AbpIdentityAspNetCoreModule), - typeof(AbpAutoMapperModule), + typeof(AbpMapperlyModule), typeof(AbpAspNetCoreMvcUiThemeSharedModule), typeof(AbpExceptionHandlingModule) )] public class AbpAccountWebModule : AbpModule { private readonly static OneTimeRunner OneTimeRunner = new OneTimeRunner(); - + public override void PreConfigureServices(ServiceConfigurationContext context) { context.Services.PreConfigure(options => @@ -59,11 +59,7 @@ public class AbpAccountWebModule : AbpModule ConfigureProfileManagementPage(); - context.Services.AddAutoMapperObjectMapper(); - Configure(options => - { - options.AddProfile(validate: true); - }); + context.Services.AddMapperlyObjectMapper(); Configure(options => { @@ -96,7 +92,7 @@ public class AbpAccountWebModule : AbpModule }); } - + public override void PostConfigureServices(ServiceConfigurationContext context) { OneTimeRunner.Run(() => diff --git a/modules/account/src/Volo.Abp.Account.Web/Volo.Abp.Account.Web.csproj b/modules/account/src/Volo.Abp.Account.Web/Volo.Abp.Account.Web.csproj index efedb773fe..3f52eae0a4 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Volo.Abp.Account.Web.csproj +++ b/modules/account/src/Volo.Abp.Account.Web/Volo.Abp.Account.Web.csproj @@ -39,7 +39,7 @@ - + diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/cs.json b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/cs.json index c530dd064b..17e549f7a7 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/cs.json +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Localization/Resources/cs.json @@ -15,6 +15,7 @@ "CmsKit.Ratings": "Hodnocení", "CmsKit.Reactions": "Reakce", "CmsKit.Tags": "Tagy", + "CmsKit.MarkedItems": "Označené položky", "CmsKit:0002": "Obsah již existuje!", "CmsKit:0003": "Entitu {0} nelze označit.", "CmsKit:Blog:0001": "Daný slimák ({Slug}) již existuje!", @@ -26,8 +27,19 @@ "CmsKit:Rating:0001": "Entitu {EntityType} nelze hodnotit.", "CmsKit:Reaction:0001": "Entita {EntityType} nemůže mít reakce.", "CmsKit:Tag:0002": "Entitu nelze označit!", + "CmsKit:MarkedItem:ToggleConfirmation": "Opravdu chcete přepnout označenou položku?", + "ToggleFavorite": "Přidat/Odebrat z oblíbených", + "FavoritesFilterMessage": "Pro filtrování oblíbených se přihlaste", + "FilterOnFavorites": "Filtrovat podle oblíbených", "CommentAuthorizationExceptionMessage": "Tyto komentáře nejsou povoleny pro veřejné zobrazení.", + "CmsKit:Modals:Login": "Přihlášení", + "CmsKit:Modals:LoginModalDefaultMessage": "Pro pokračování se přihlaste!", + "CmsKit:Modals:YouAreNotAuthenticated": "Tato operace pro vás není autorizována.", "CommentDeletionConfirmationMessage": "Tento komentář a všechny odpovědi budou smazány!", + "CmsKit:MarkedItem:0001": "Entitu {EntityType} nelze označit.", + "CmsKit:MarkedItem:0002": "Pro typ entity '{EntityType}' nebyla nalezena žádná definice.", + "CmsKit:MarkedItem:0003": "Definice pro typ entity '{EntityType}' již existuje. Každý typ entity by měl mít pouze jednu definici.", + "CmsKit:MarkedItem:LoginMessage": "Pro označení této položky se přihlaste.", "Comments": "Komentáře", "Content": "Obsah", "ContentDeletionConfirmationMessage": "Opravdu chcete tento obsah smazat?", @@ -134,6 +146,7 @@ "SamplePageMessage": "Ukázková stránka pro modul Pro", "SaveChanges": "Uložit změny", "Script": "Skript", + "SelectLayout": "Vybrat rozložení", "SelectAll": "Vybrat vše", "Send": "Poslat", "SendMessage": "Poslat zprávu", @@ -216,6 +229,8 @@ "Feature:ReactionEnableDescription": "Systém reakcí CMS Kit, který umožňuje uživatelům posílat reakce entitám, jako jsou BlogPost, Komentáře atd.", "Feature:TagEnable": "Označování", "Feature:TagEnableDescription": "Systém značek CMS Kit, který umožňuje označování entit, jako je BlogPost.", + "Feature:MarkedItemEnable": "Označená položka", + "Feature:MarkedItemEnableDescription": "Systém označování CMS Kit, který umožňuje uživatelům označovat entity jako oblíbené.", "DeleteBlogPostMessage": "Blog bude smazán. Jsi si jistá?", "CaptchaCode": "Captcha kód", "CommentTextRequired": "Komentář je povinný", @@ -230,6 +245,37 @@ "TagsHelpText": "Značky by měly být odděleny čárkami (např.: tag1, tag2, tag3)", "ThisPartOfContentCouldntBeLoaded": "Tato část obsahu nemohla být načtena.", "DuplicateCommentAttemptMessage": "Byl zjištěn duplicitní pokus o vložení komentáře. Váš komentář již byl odeslán.", + "NoBlogPostYet": "Zatím žádný blogový příspěvek!", + "CmsKit:Comment": "Komentář", + "CmsKitCommentOptions:RequireApprovement": "Vyžadovat schválení komentářů", + "CmsKitCommentOptions:RequireApprovementDescription": "Když je povoleno, komentáře budou vyžadovat schválení před zveřejněním.", + "CommentFilter:ApproveState": "Stav schválení", + "ApproveState": "Stav schválení", + "CommentFilter:0": "Vše", + "CommentFilter:1": "Schváleno", + "CommentFilter:2": "Zamítnuto", + "CommentFilter:4": "Čeká", + "ApprovedSuccessfully": "Úspěšně schváleno", + "ApprovalRevokedSuccessfully": "Schválení úspěšně odvoláno", + "Approve": "Schválit", + "Disapproved": "Zamítnuto", + "CommentAlertMessage": "Čeká {0} komentářů na schválení", + "Settings:Menu:CmsKit": "CMS", + "CommentsAwaitingApproval": "Komentáře čekající na schválení", + "JustNow": "Právě teď", + "MinuteAgo": "před 1 minutou", + "MinutesAgo": "před {0} minutami", + "HourAgo": "před 1 hodinou", + "HoursAgo": "před {0} hodinami", + "YesterdayAt": "Včera v {0}", + "DayAt": "{0} v {1}", + "MonthDayAt": "{0} {1} v {2}", + "FullDate": "{0} {1} {2}", + "Minute": "m", + "Hour": "h", + "Day": "d", + "Week": "t", + "CommentSubmittedForApproval": "Váš komentář byl odeslán ke schválení.", "ChooseAnActionForBlog": "Vyberte akci pro blog", "AssignBlogPostsToOtherBlog": "Přiřaďte blogové příspěvky k jinému blogu", "SelectAnBlogToAssign": "Vyberte blog, ke kterému chcete přiřadit blogové příspěvky", diff --git a/modules/identity/src/Volo.Abp.Identity.Application/Volo.Abp.Identity.Application.csproj b/modules/identity/src/Volo.Abp.Identity.Application/Volo.Abp.Identity.Application.csproj index 0b3693b5d1..7125af8060 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application/Volo.Abp.Identity.Application.csproj +++ b/modules/identity/src/Volo.Abp.Identity.Application/Volo.Abp.Identity.Application.csproj @@ -18,7 +18,7 @@ - + diff --git a/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/AbpIdentityApplicationMappers.cs b/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/AbpIdentityApplicationMappers.cs new file mode 100644 index 0000000000..164da27aae --- /dev/null +++ b/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/AbpIdentityApplicationMappers.cs @@ -0,0 +1,19 @@ +using Riok.Mapperly.Abstractions; +using Volo.Abp.Identity; +using Volo.Abp.Mapperly; + +[Mapper] +[MapExtraProperties] +public partial class IdentityUserToIdentityUserDtoMapper : MapperBase +{ + public override partial IdentityUserDto Map(IdentityUser source); + public override partial void Map(IdentityUser source, IdentityUserDto destination); +} + +[Mapper] +[MapExtraProperties] +public partial class IdentityRoleToIdentityRoleDtoMapper : MapperBase +{ + public override partial IdentityRoleDto Map(IdentityRole source); + public override partial void Map(IdentityRole source, IdentityRoleDto destination); +} diff --git a/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/AbpIdentityApplicationModule.cs b/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/AbpIdentityApplicationModule.cs index 673a0605c7..07865ab8f5 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/AbpIdentityApplicationModule.cs +++ b/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/AbpIdentityApplicationModule.cs @@ -1,5 +1,5 @@ using Microsoft.Extensions.DependencyInjection; -using Volo.Abp.AutoMapper; +using Volo.Abp.Mapperly; using Volo.Abp.Modularity; using Volo.Abp.PermissionManagement; @@ -8,18 +8,13 @@ namespace Volo.Abp.Identity; [DependsOn( typeof(AbpIdentityDomainModule), typeof(AbpIdentityApplicationContractsModule), - typeof(AbpAutoMapperModule), + typeof(AbpMapperlyModule), typeof(AbpPermissionManagementApplicationModule) )] public class AbpIdentityApplicationModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { - context.Services.AddAutoMapperObjectMapper(); - - Configure(options => - { - options.AddProfile(validate: true); - }); + context.Services.AddMapperlyObjectMapper(); } } diff --git a/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/AbpIdentityApplicationModuleAutoMapperProfile.cs b/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/AbpIdentityApplicationModuleAutoMapperProfile.cs deleted file mode 100644 index a546a9bca1..0000000000 --- a/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/AbpIdentityApplicationModuleAutoMapperProfile.cs +++ /dev/null @@ -1,15 +0,0 @@ -using AutoMapper; - -namespace Volo.Abp.Identity; - -public class AbpIdentityApplicationModuleAutoMapperProfile : Profile -{ - public AbpIdentityApplicationModuleAutoMapperProfile() - { - CreateMap() - .MapExtraProperties(); - - CreateMap() - .MapExtraProperties(); - } -} diff --git a/modules/identity/src/Volo.Abp.Identity.Blazor/AbpIdentityBlazorAutoMapperProfile.cs b/modules/identity/src/Volo.Abp.Identity.Blazor/AbpIdentityBlazorAutoMapperProfile.cs deleted file mode 100644 index 52a450785e..0000000000 --- a/modules/identity/src/Volo.Abp.Identity.Blazor/AbpIdentityBlazorAutoMapperProfile.cs +++ /dev/null @@ -1,18 +0,0 @@ -using AutoMapper; -using Volo.Abp.AutoMapper; - -namespace Volo.Abp.Identity.Blazor; - -public class AbpIdentityBlazorAutoMapperProfile : Profile -{ - public AbpIdentityBlazorAutoMapperProfile() - { - CreateMap() - .MapExtraProperties() - .Ignore(x => x.Password) - .Ignore(x => x.RoleNames); - - CreateMap() - .MapExtraProperties(); - } -} diff --git a/modules/identity/src/Volo.Abp.Identity.Blazor/AbpIdentityBlazorMappers.cs b/modules/identity/src/Volo.Abp.Identity.Blazor/AbpIdentityBlazorMappers.cs new file mode 100644 index 0000000000..614ca6ae32 --- /dev/null +++ b/modules/identity/src/Volo.Abp.Identity.Blazor/AbpIdentityBlazorMappers.cs @@ -0,0 +1,25 @@ +using Riok.Mapperly.Abstractions; +using Volo.Abp.Mapperly; + +namespace Volo.Abp.Identity.Blazor; + +[Mapper] +[MapExtraProperties] +public partial class IdentityUserDtoToIdentityUserUpdateDtoMapper : MapperBase +{ + [MapperIgnoreTarget(nameof(IdentityUserUpdateDto.Password))] + [MapperIgnoreTarget(nameof(IdentityUserUpdateDto.RoleNames))] + public override partial IdentityUserUpdateDto Map(IdentityUserDto source); + + [MapperIgnoreTarget(nameof(IdentityUserUpdateDto.Password))] + [MapperIgnoreTarget(nameof(IdentityUserUpdateDto.RoleNames))] + public override partial void Map(IdentityUserDto source, IdentityUserUpdateDto destination); +} + +[Mapper] +[MapExtraProperties] +public partial class IdentityRoleDtoToIdentityRoleUpdateDtoMapper : MapperBase +{ + public override partial IdentityRoleUpdateDto Map(IdentityRoleDto source); + public override partial void Map(IdentityRoleDto source, IdentityRoleUpdateDto destination); +} diff --git a/modules/identity/src/Volo.Abp.Identity.Blazor/AbpIdentityBlazorModule.cs b/modules/identity/src/Volo.Abp.Identity.Blazor/AbpIdentityBlazorModule.cs index 3367b42a1b..89aba05f89 100644 --- a/modules/identity/src/Volo.Abp.Identity.Blazor/AbpIdentityBlazorModule.cs +++ b/modules/identity/src/Volo.Abp.Identity.Blazor/AbpIdentityBlazorModule.cs @@ -1,7 +1,7 @@ using Localization.Resources.AbpUi; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AspNetCore.Components.Web.Theming.Routing; -using Volo.Abp.AutoMapper; +using Volo.Abp.Mapperly; using Volo.Abp.BlazoriseUI; using Volo.Abp.Identity.Localization; using Volo.Abp.Localization; @@ -16,7 +16,7 @@ namespace Volo.Abp.Identity.Blazor; [DependsOn( typeof(AbpIdentityApplicationContractsModule), - typeof(AbpAutoMapperModule), + typeof(AbpMapperlyModule), typeof(AbpPermissionManagementBlazorModule), typeof(AbpBlazoriseUIModule) )] @@ -26,12 +26,7 @@ public class AbpIdentityBlazorModule : AbpModule public override void ConfigureServices(ServiceConfigurationContext context) { - context.Services.AddAutoMapperObjectMapper(); - - Configure(options => - { - options.AddProfile(validate: true); - }); + context.Services.AddMapperlyObjectMapper(); Configure(options => { @@ -42,7 +37,7 @@ public class AbpIdentityBlazorModule : AbpModule { options.AdditionalAssemblies.Add(typeof(AbpIdentityBlazorModule).Assembly); }); - + Configure(options => { options.Resources diff --git a/modules/identity/src/Volo.Abp.Identity.Blazor/Volo.Abp.Identity.Blazor.csproj b/modules/identity/src/Volo.Abp.Identity.Blazor/Volo.Abp.Identity.Blazor.csproj index 355cb4294a..777fb1c31e 100644 --- a/modules/identity/src/Volo.Abp.Identity.Blazor/Volo.Abp.Identity.Blazor.csproj +++ b/modules/identity/src/Volo.Abp.Identity.Blazor/Volo.Abp.Identity.Blazor.csproj @@ -8,7 +8,7 @@ - + diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo.Abp.Identity.Domain.csproj b/modules/identity/src/Volo.Abp.Identity.Domain/Volo.Abp.Identity.Domain.csproj index fadd6e4109..a1bde79665 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo.Abp.Identity.Domain.csproj +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo.Abp.Identity.Domain.csproj @@ -23,7 +23,7 @@ - + diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityDomainModule.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityDomainModule.cs index baf0871c58..590beb8e09 100644 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityDomainModule.cs +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpIdentityDomainModule.cs @@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; -using Volo.Abp.AutoMapper; +using Volo.Abp.Mapperly; using Volo.Abp.Domain; using Volo.Abp.Domain.Entities.Events.Distributed; using Volo.Abp.Modularity; @@ -19,7 +19,7 @@ namespace Volo.Abp.Identity; typeof(AbpDddDomainModule), typeof(AbpIdentityDomainSharedModule), typeof(AbpUsersDomainModule), - typeof(AbpAutoMapperModule) + typeof(AbpMapperlyModule) )] public class AbpIdentityDomainModule : AbpModule { @@ -35,12 +35,7 @@ public class AbpIdentityDomainModule : AbpModule public override void ConfigureServices(ServiceConfigurationContext context) { - context.Services.AddAutoMapperObjectMapper(); - - Configure(options => - { - options.AddProfile(validate: true); - }); + context.Services.AddMapperlyObjectMapper(); Configure(options => { diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDomainMappers.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDomainMappers.cs new file mode 100644 index 0000000000..f7d41c9891 --- /dev/null +++ b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDomainMappers.cs @@ -0,0 +1,33 @@ +using Riok.Mapperly.Abstractions; +using Volo.Abp.Mapperly; +using Volo.Abp.Users; + +namespace Volo.Abp.Identity; + +[Mapper] +public partial class IdentityUserToUserEtoMapper : MapperBase +{ + public override partial UserEto Map(IdentityUser source); + public override partial void Map(IdentityUser source, UserEto destination); +} + +[Mapper] +public partial class IdentityClaimTypeToIdentityClaimTypeEtoMapper : MapperBase +{ + public override partial IdentityClaimTypeEto Map(IdentityClaimType source); + public override partial void Map(IdentityClaimType source, IdentityClaimTypeEto destination); +} + +[Mapper] +public partial class IdentityRoleToIdentityRoleEtoMapper : MapperBase +{ + public override partial IdentityRoleEto Map(IdentityRole source); + public override partial void Map(IdentityRole source, IdentityRoleEto destination); +} + +[Mapper] +public partial class OrganizationUnitToOrganizationUnitEtoMapper : MapperBase +{ + public override partial OrganizationUnitEto Map(OrganizationUnit source); + public override partial void Map(OrganizationUnit source, OrganizationUnitEto destination); +} diff --git a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDomainMappingProfile.cs b/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDomainMappingProfile.cs deleted file mode 100644 index ae9e5b5065..0000000000 --- a/modules/identity/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityDomainMappingProfile.cs +++ /dev/null @@ -1,15 +0,0 @@ -using AutoMapper; -using Volo.Abp.Users; - -namespace Volo.Abp.Identity; - -public class IdentityDomainMappingProfile : Profile -{ - public IdentityDomainMappingProfile() - { - CreateMap(); - CreateMap(); - CreateMap(); - CreateMap(); - } -} diff --git a/modules/identity/src/Volo.Abp.Identity.Web/AbpIdentityWebAutoMapperProfile.cs b/modules/identity/src/Volo.Abp.Identity.Web/AbpIdentityWebAutoMapperProfile.cs deleted file mode 100644 index e032b3352f..0000000000 --- a/modules/identity/src/Volo.Abp.Identity.Web/AbpIdentityWebAutoMapperProfile.cs +++ /dev/null @@ -1,57 +0,0 @@ -using AutoMapper; -using Volo.Abp.AutoMapper; -using Volo.Abp.Identity.Web.Pages.Identity.Roles; -using CreateUserModalModel = Volo.Abp.Identity.Web.Pages.Identity.Users.CreateModalModel; -using EditUserModalModel = Volo.Abp.Identity.Web.Pages.Identity.Users.EditModalModel; - -namespace Volo.Abp.Identity.Web; - -public class AbpIdentityWebAutoMapperProfile : Profile -{ - public AbpIdentityWebAutoMapperProfile() - { - CreateUserMappings(); - CreateRoleMappings(); - } - - protected virtual void CreateUserMappings() - { - //List - CreateMap() - .Ignore(x => x.Password); - - //CreateModal - CreateMap() - .MapExtraProperties() - .ForMember(dest => dest.RoleNames, opt => opt.Ignore()); - - CreateMap() - .ForMember(dest => dest.IsAssigned, opt => opt.Ignore()); - - //EditModal - CreateMap() - .MapExtraProperties() - .ForMember(dest => dest.RoleNames, opt => opt.Ignore()); - - CreateMap() - .ForMember(dest => dest.IsAssigned, opt => opt.Ignore()); - - CreateMap() - .ForMember(dest => dest.CreatedBy, opt => opt.Ignore()) - .ForMember(dest => dest.ModifiedBy, opt => opt.Ignore()); - } - - protected virtual void CreateRoleMappings() - { - //List - CreateMap(); - - //CreateModal - CreateMap() - .MapExtraProperties(); - - //EditModal - CreateMap() - .MapExtraProperties(); - } -} diff --git a/modules/identity/src/Volo.Abp.Identity.Web/AbpIdentityWebMappers.cs b/modules/identity/src/Volo.Abp.Identity.Web/AbpIdentityWebMappers.cs new file mode 100644 index 0000000000..1877dac1d8 --- /dev/null +++ b/modules/identity/src/Volo.Abp.Identity.Web/AbpIdentityWebMappers.cs @@ -0,0 +1,94 @@ +using Riok.Mapperly.Abstractions; +using Volo.Abp.Mapperly; +using Volo.Abp.Identity.Web.Pages.Identity.Roles; +using CreateUserModalModel = Volo.Abp.Identity.Web.Pages.Identity.Users.CreateModalModel; +using EditUserModalModel = Volo.Abp.Identity.Web.Pages.Identity.Users.EditModalModel; + +namespace Volo.Abp.Identity.Web; + +[Mapper] +public partial class IdentityUserDtoToEditUserModalModelUserInfoViewModelMapper : MapperBase +{ + [MapperIgnoreTarget(nameof(EditUserModalModel.UserInfoViewModel.Password))] + public override partial EditUserModalModel.UserInfoViewModel Map(IdentityUserDto source); + + [MapperIgnoreTarget(nameof(EditUserModalModel.UserInfoViewModel.Password))] + public override partial void Map(IdentityUserDto source, EditUserModalModel.UserInfoViewModel destination); +} + +[Mapper] +[MapExtraProperties] +public partial class CreateUserModalModelUserInfoViewModelToIdentityUserCreateDtoMapper : MapperBase +{ + [MapperIgnoreTarget(nameof(IdentityUserCreateDto.RoleNames))] + public override partial IdentityUserCreateDto Map(CreateUserModalModel.UserInfoViewModel source); + + [MapperIgnoreTarget(nameof(IdentityUserCreateDto.RoleNames))] + public override partial void Map(CreateUserModalModel.UserInfoViewModel source, IdentityUserCreateDto destination); +} + +[Mapper] +[MapExtraProperties] +public partial class IdentityRoleDtoToCreateUserModalModelAssignedRoleViewModelMapper : MapperBase +{ + [MapperIgnoreTarget(nameof(CreateUserModalModel.AssignedRoleViewModel.IsAssigned))] + public override partial CreateUserModalModel.AssignedRoleViewModel Map(IdentityRoleDto source); + + [MapperIgnoreTarget(nameof(CreateUserModalModel.AssignedRoleViewModel.IsAssigned))] + public override partial void Map(IdentityRoleDto source, CreateUserModalModel.AssignedRoleViewModel destination); +} + +[Mapper] +public partial class EditUserModalModelUserInfoViewModelToIdentityUserUpdateDtoMapper : MapperBase +{ + [MapperIgnoreTarget(nameof(IdentityUserUpdateDto.RoleNames))] + public override partial IdentityUserUpdateDto Map(EditUserModalModel.UserInfoViewModel source); + + [MapperIgnoreTarget(nameof(IdentityUserUpdateDto.RoleNames))] + public override partial void Map(EditUserModalModel.UserInfoViewModel source, IdentityUserUpdateDto destination); +} + +[Mapper] +public partial class IdentityRoleDtoToEditUserModalModelAssignedRoleViewModelMapper : MapperBase +{ + [MapperIgnoreTarget(nameof(EditUserModalModel.AssignedRoleViewModel.IsAssigned))] + public override partial EditUserModalModel.AssignedRoleViewModel Map(IdentityRoleDto source); + + [MapperIgnoreTarget(nameof(EditUserModalModel.AssignedRoleViewModel.IsAssigned))] + public override partial void Map(IdentityRoleDto source, EditUserModalModel.AssignedRoleViewModel destination); +} + +[Mapper] +public partial class IdentityUserDtoToEditUserModalModelDetailViewModelMapper : MapperBase +{ + [MapperIgnoreTarget(nameof(EditUserModalModel.DetailViewModel.CreatedBy))] + [MapperIgnoreTarget(nameof(EditUserModalModel.DetailViewModel.ModifiedBy))] + public override partial EditUserModalModel.DetailViewModel Map(IdentityUserDto source); + + [MapperIgnoreTarget(nameof(EditUserModalModel.DetailViewModel.CreatedBy))] + [MapperIgnoreTarget(nameof(EditUserModalModel.DetailViewModel.ModifiedBy))] + public override partial void Map(IdentityUserDto source, EditUserModalModel.DetailViewModel destination); +} + +[Mapper] +public partial class IdentityRoleDtoToEditModalModelRoleInfoModelMapper : MapperBase +{ + public override partial EditModalModel.RoleInfoModel Map(IdentityRoleDto source); + public override partial void Map(IdentityRoleDto source, EditModalModel.RoleInfoModel destination); +} + +[Mapper] +[MapExtraProperties] +public partial class CreateModalModelRoleInfoModelToIdentityRoleCreateDtoMapper : MapperBase +{ + public override partial IdentityRoleCreateDto Map(CreateModalModel.RoleInfoModel source); + public override partial void Map(CreateModalModel.RoleInfoModel source, IdentityRoleCreateDto destination); +} + +[Mapper] +[MapExtraProperties] +public partial class EditModalModelRoleInfoModelToIdentityRoleUpdateDtoMapper : MapperBase +{ + public override partial IdentityRoleUpdateDto Map(EditModalModel.RoleInfoModel source); + public override partial void Map(EditModalModel.RoleInfoModel source, IdentityRoleUpdateDto destination); +} diff --git a/modules/identity/src/Volo.Abp.Identity.Web/AbpIdentityWebModule.cs b/modules/identity/src/Volo.Abp.Identity.Web/AbpIdentityWebModule.cs index ae3e8ebc72..6d2faf6467 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/AbpIdentityWebModule.cs +++ b/modules/identity/src/Volo.Abp.Identity.Web/AbpIdentityWebModule.cs @@ -3,7 +3,7 @@ using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AspNetCore.Mvc.Localization; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.PageToolbars; -using Volo.Abp.AutoMapper; +using Volo.Abp.Mapperly; using Volo.Abp.Http.ProxyScripting.Generators.JQuery; using Volo.Abp.Identity.Localization; using Volo.Abp.Identity.Web.Navigation; @@ -19,7 +19,7 @@ using Volo.Abp.Threading; namespace Volo.Abp.Identity.Web; [DependsOn(typeof(AbpIdentityApplicationContractsModule))] -[DependsOn(typeof(AbpAutoMapperModule))] +[DependsOn(typeof(AbpMapperlyModule))] [DependsOn(typeof(AbpPermissionManagementWebModule))] [DependsOn(typeof(AbpAspNetCoreMvcUiThemeSharedModule))] public class AbpIdentityWebModule : AbpModule @@ -51,12 +51,7 @@ public class AbpIdentityWebModule : AbpModule options.FileSets.AddEmbedded(); }); - context.Services.AddAutoMapperObjectMapper(); - - Configure(options => - { - options.AddProfile(validate: true); - }); + context.Services.AddMapperlyObjectMapper(); Configure(options => { diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Volo.Abp.Identity.Web.csproj b/modules/identity/src/Volo.Abp.Identity.Web/Volo.Abp.Identity.Web.csproj index 8a77024b52..54bd5fd28b 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Volo.Abp.Identity.Web.csproj +++ b/modules/identity/src/Volo.Abp.Identity.Web/Volo.Abp.Identity.Web.csproj @@ -40,7 +40,7 @@ - + diff --git a/npm/ng-packs/packages/core/src/lib/abstracts/auth.guard.ts b/npm/ng-packs/packages/core/src/lib/abstracts/auth.guard.ts index 50473e4df1..020f750555 100644 --- a/npm/ng-packs/packages/core/src/lib/abstracts/auth.guard.ts +++ b/npm/ng-packs/packages/core/src/lib/abstracts/auth.guard.ts @@ -21,3 +21,9 @@ export const authGuard: CanActivateFn = () => { console.error('You should add @abp/ng-oauth packages or create your own auth packages.'); return false; }; + + +export const asyncAuthGuard: CanActivateFn = () => { + console.error('You should add @abp/ng-oauth packages or create your own auth packages.'); + return false; +}; \ No newline at end of file diff --git a/npm/ng-packs/packages/core/src/lib/providers/core-module-config.provider.ts b/npm/ng-packs/packages/core/src/lib/providers/core-module-config.provider.ts index fafb51ff6d..e8ca5ea711 100644 --- a/npm/ng-packs/packages/core/src/lib/providers/core-module-config.provider.ts +++ b/npm/ng-packs/packages/core/src/lib/providers/core-module-config.provider.ts @@ -1,4 +1,4 @@ -import { makeEnvironmentProviders, Provider, inject, provideAppInitializer } from '@angular/core'; +import { makeEnvironmentProviders, Provider, provideAppInitializer, inject } from '@angular/core'; import { TitleStrategy } from '@angular/router'; import { HTTP_INTERCEPTORS, @@ -23,7 +23,7 @@ import { ABP, SortableItem } from '../models'; import { AuthErrorFilterService } from '../abstracts'; import { DEFAULT_DYNAMIC_LAYOUTS } from '../constants'; import { LocalizationService, LocalStorageListenerService, AbpTitleStrategy } from '../services'; -import { DefaultQueueManager, getInitialData, localeInitializer } from '../utils'; +import { DefaultQueueManager, getInitialData } from '../utils'; import { CookieLanguageProvider, IncludeLocalizationResourcesProvider, LocaleProvider } from './'; import { TimezoneInterceptor } from '../interceptors'; @@ -106,14 +106,12 @@ export function provideAbpCore(...features: CoreFeature[]) { headerName: 'RequestVerificationToken', }), ), - provideAppInitializer(() => { - getInitialData(); - localeInitializer(); + provideAppInitializer(async () => { inject(LocalizationService); inject(LocalStorageListenerService); inject(RoutesHandler); + await getInitialData(); }), - LocaleProvider, CookieLanguageProvider, { diff --git a/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts b/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts index 3bc9b6f2b5..0c65073ef6 100644 --- a/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts +++ b/npm/ng-packs/packages/core/src/lib/utils/initial-utils.ts @@ -50,12 +50,13 @@ export async function getInitialData() { }), ); await lastValueFrom(result$); + await localeInitializer(injector); } -export function localeInitializer() { - const injector = inject(Injector); - const sessionState = injector.get(SessionStateService); - const { registerLocaleFn }: ABP.Root = injector.get(CORE_OPTIONS); +export function localeInitializer(injector?: Injector) { + const currentInjector = injector || inject(Injector); + const sessionState = currentInjector.get(SessionStateService); + const { registerLocaleFn }: ABP.Root = currentInjector.get(CORE_OPTIONS); const lang = sessionState.getLanguage() || 'en'; diff --git a/npm/ng-packs/packages/oauth/src/lib/guards/oauth.guard.ts b/npm/ng-packs/packages/oauth/src/lib/guards/oauth.guard.ts index 6f751804cd..1311a8d7a8 100644 --- a/npm/ng-packs/packages/oauth/src/lib/guards/oauth.guard.ts +++ b/npm/ng-packs/packages/oauth/src/lib/guards/oauth.guard.ts @@ -6,10 +6,10 @@ import { CanActivateFn, } from '@angular/router'; -import { Observable } from 'rxjs'; +import { Observable, timer, filter, take, map, firstValueFrom, timeout, catchError, of } from 'rxjs'; import { OAuthService } from 'angular-oauth2-oidc'; -import { AuthService, IAbpGuard } from '@abp/ng.core'; +import { AuthService, IAbpGuard, EnvironmentService } from '@abp/ng.core'; /** * @deprecated Use `abpOAuthGuard` *function* instead. @@ -53,3 +53,36 @@ export const abpOAuthGuard: CanActivateFn = ( authService.navigateToLogin(params); return false; }; + +export const asyncAbpOAuthGuard: CanActivateFn = ( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot, +) => { + const oAuthService = inject(OAuthService); + const authService = inject(AuthService); + const environmentService = inject(EnvironmentService); + + const { oAuthConfig } = environmentService.getEnvironment(); + + if (oAuthConfig?.responseType === 'code') { + return firstValueFrom( + timer(0, 100).pipe( + map(() => oAuthService.hasValidAccessToken()), + filter(Boolean), + take(1), + timeout(3000), + catchError(() => { + authService.navigateToLogin({ returnUrl: state.url }); + return of(false); + }) + ) + ); + } + + if (oAuthService.hasValidAccessToken()) { + return true; + } + + authService.navigateToLogin({ returnUrl: state.url }); + return false; +}; diff --git a/npm/ng-packs/packages/oauth/src/lib/providers/oauth-module-config.provider.ts b/npm/ng-packs/packages/oauth/src/lib/providers/oauth-module-config.provider.ts index 92ecc86012..534882dd13 100644 --- a/npm/ng-packs/packages/oauth/src/lib/providers/oauth-module-config.provider.ts +++ b/npm/ng-packs/packages/oauth/src/lib/providers/oauth-module-config.provider.ts @@ -2,6 +2,7 @@ import { AuthService, AuthGuard, authGuard, + asyncAuthGuard, ApiInterceptor, PIPE_TO_LOGIN_FN_KEY, CHECK_AUTHENTICATION_STATE_FN_KEY, @@ -11,7 +12,7 @@ import { import { Provider, makeEnvironmentProviders, inject, provideAppInitializer } from '@angular/core'; import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { OAuthModule, OAuthStorage } from 'angular-oauth2-oidc'; -import { AbpOAuthGuard, abpOAuthGuard } from '../guards'; +import { AbpOAuthGuard, abpOAuthGuard, asyncAbpOAuthGuard, } from '../guards'; import { OAuthConfigurationHandler } from '../handlers'; import { OAuthApiInterceptor } from '../interceptors'; import { AbpOAuthService, OAuthErrorFilterService } from '../services'; @@ -31,6 +32,10 @@ export function provideAbpOAuth() { { provide: authGuard, useValue: abpOAuthGuard, + }, + { + provide: asyncAuthGuard, + useValue: asyncAbpOAuthGuard, }, { provide: ApiInterceptor,