Browse Source

Added option to validate an automapper profile independent from other configuration.

pull/179/head
Halil İbrahim Kalkan 9 years ago
parent
commit
63a59db44b
  1. 12
      src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AbpAutoMapperModule.cs
  2. 19
      src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AbpAutoMapperOptions.cs
  3. 4
      src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateOrUpdateDtoBase.cs
  4. 4
      src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserDto.cs
  5. 9
      src/Volo.Abp.Identity.Web/AbpIdentityWebModule.cs
  6. 4
      src/Volo.Abp.Identity.Web/Areas/Identity/Localization/Resource/en.json
  7. 4
      src/Volo.Abp.Identity.Web/Areas/Identity/Localization/Resource/tr.json
  8. 12
      src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml
  9. 6
      src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml.cs
  10. 12
      src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml
  11. 6
      src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml.cs
  12. 2
      src/Volo.Abp.TestBase/AbpIntegratedTest.cs
  13. 73
      test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AutoMapper_ConfigurationValidation_Tests.cs
  14. 5
      test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AutofacTestModule.cs

12
src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AbpAutoMapperModule.cs

@ -46,6 +46,14 @@ namespace Volo.Abp.AutoMapper
} }
} }
void ValidateAll(IConfigurationProvider config)
{
foreach (var profileType in options.ValidatingProfiles)
{
config.AssertConfigurationIsValid(((Profile)Activator.CreateInstance(profileType)).ProfileName);
}
}
if (options.UseStaticMapper) if (options.UseStaticMapper)
{ {
//We should prevent duplicate mapping in an application, since Mapper is static. //We should prevent duplicate mapping in an application, since Mapper is static.
@ -56,6 +64,8 @@ namespace Volo.Abp.AutoMapper
ConfigureAll(new AbpAutoMapperConfigurationContext(mapperConfigurationExpression, scope.ServiceProvider)); ConfigureAll(new AbpAutoMapperConfigurationContext(mapperConfigurationExpression, scope.ServiceProvider));
}); });
ValidateAll(Mapper.Configuration);
_createdMappingsBefore = true; _createdMappingsBefore = true;
} }
@ -68,6 +78,8 @@ namespace Volo.Abp.AutoMapper
ConfigureAll(new AbpAutoMapperConfigurationContext(mapperConfigurationExpression, scope.ServiceProvider)); ConfigureAll(new AbpAutoMapperConfigurationContext(mapperConfigurationExpression, scope.ServiceProvider));
}); });
ValidateAll(config);
scope.ServiceProvider.GetRequiredService<MapperAccessor>().Mapper = config.CreateMapper(); scope.ServiceProvider.GetRequiredService<MapperAccessor>().Mapper = config.CreateMapper();
} }
} }

19
src/Volo.Abp.AutoMapper/Volo/Abp/AutoMapper/AbpAutoMapperOptions.cs

@ -1,5 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using AutoMapper;
using Volo.Abp.Collections;
namespace Volo.Abp.AutoMapper namespace Volo.Abp.AutoMapper
{ {
@ -7,12 +9,29 @@ namespace Volo.Abp.AutoMapper
{ {
public List<Action<IAbpAutoMapperConfigurationContext>> Configurators { get; } public List<Action<IAbpAutoMapperConfigurationContext>> Configurators { get; }
public ITypeList<Profile> ValidatingProfiles { get; set; }
public bool UseStaticMapper { get; set; } public bool UseStaticMapper { get; set; }
public AbpAutoMapperOptions() public AbpAutoMapperOptions()
{ {
UseStaticMapper = true; UseStaticMapper = true;
Configurators = new List<Action<IAbpAutoMapperConfigurationContext>>(); Configurators = new List<Action<IAbpAutoMapperConfigurationContext>>();
ValidatingProfiles = new TypeList<Profile>();
}
public void AddProfile<TProfile>(bool validate = false)
where TProfile: Profile, new()
{
Configurators.Add(context =>
{
context.MapperConfiguration.AddProfile<TProfile>();
});
if (validate)
{
ValidatingProfiles.Add<TProfile>();
}
} }
} }
} }

4
src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserCreateOrUpdateDtoBase.cs

@ -17,9 +17,9 @@ namespace Volo.Abp.Identity
[MaxLength(IdentityUserConsts.MaxPhoneNumberLength)] [MaxLength(IdentityUserConsts.MaxPhoneNumberLength)]
public string PhoneNumber { get; set; } public string PhoneNumber { get; set; }
public bool TwoFactorEnabled { get; set; } //TODO: Optional? public bool TwoFactorEnabled { get; set; }
public bool LockoutEnabled { get; set; } //TODO: Optional? public bool LockoutEnabled { get; set; }
[CanBeNull] [CanBeNull]
public string[] RoleNames { get; set; } public string[] RoleNames { get; set; }

4
src/Volo.Abp.Identity.Application.Contracts/Volo/Abp/Identity/IdentityUserDto.cs

@ -17,8 +17,8 @@ namespace Volo.Abp.Identity
public bool TwoFactorEnabled { get; set; } public bool TwoFactorEnabled { get; set; }
public DateTimeOffset? LockoutEnd { get; set; }
public bool LockoutEnabled { get; set; } public bool LockoutEnabled { get; set; }
public DateTimeOffset? LockoutEnd { get; set; }
} }
} }

9
src/Volo.Abp.Identity.Web/AbpIdentityWebModule.cs

@ -1,6 +1,4 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.Localization; using Volo.Abp.AspNetCore.Mvc.Localization;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap;
using Volo.Abp.AutoMapper; using Volo.Abp.AutoMapper;
@ -47,10 +45,7 @@ namespace Volo.Abp.Identity.Web
services.Configure<AbpAutoMapperOptions>(options => services.Configure<AbpAutoMapperOptions>(options =>
{ {
options.Configurators.Add(context => options.AddProfile<AbpIdentityWebAutoMapperProfile>(true);
{
context.MapperConfiguration.AddProfile<AbpIdentityWebAutoMapperProfile>();
});
}); });
} }
} }

4
src/Volo.Abp.Identity.Web/Areas/Identity/Localization/Resource/en.json

@ -14,6 +14,8 @@
"Password": "Password", "Password": "Password",
"UserDeletionConfirmationMessage": "User '{0}' will be deleted. Do you confirm that?", "UserDeletionConfirmationMessage": "User '{0}' will be deleted. Do you confirm that?",
"MaxLengthKey": "The field {0} must be a string or array type with a maximum length of '{1}'.", "MaxLengthKey": "The field {0} must be a string or array type with a maximum length of '{1}'.",
"The {0} field is required.": "The {0} field is required." "The {0} field is required.": "The {0} field is required.",
"TwoFactorVerification": "Two factor verification",
"AccountLockoutOnFailedLoginAttempts": "Locking account after failed login attempts"
} }
} }

4
src/Volo.Abp.Identity.Web/Areas/Identity/Localization/Resource/tr.json

@ -14,6 +14,8 @@
"Password": "Şifre", "Password": "Şifre",
"UserDeletionConfirmationMessage": "{0} kullanıcısı silinecektir. Onaylıyor musunuz?", "UserDeletionConfirmationMessage": "{0} kullanıcısı silinecektir. Onaylıyor musunuz?",
"The field {0} must be a string or array type with a maximum length of '{1}'.": "{0} alanı en fazla '{1}' karakter uzunluğunda olmalıdır.", "The field {0} must be a string or array type with a maximum length of '{1}'.": "{0} alanı en fazla '{1}' karakter uzunluğunda olmalıdır.",
"The {0} field is required.": "{0} alanı zorunludur." "The {0} field is required.": "{0} alanı zorunludur.",
"TwoFactorVerification": "İki aşamalı doğrumala",
"AccountLockoutOnFailedLoginAttempts": "Başarısız giriş denemeleri sonrası hesabı kilitleme"
} }
} }

12
src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml

@ -48,6 +48,18 @@
<input asp-for="UserInfo.PhoneNumber" class="form-control" /> <input asp-for="UserInfo.PhoneNumber" class="form-control" />
<span asp-validation-for="UserInfo.PhoneNumber" class="text-danger"></span> <span asp-validation-for="UserInfo.PhoneNumber" class="text-danger"></span>
</div> </div>
<div class="form-check">
<label asp-for="@Model.UserInfo.LockoutEnabled" class="form-check-label">
<input asp-for="@Model.UserInfo.LockoutEnabled" class="form-check-input" />
@Html.DisplayNameFor(m => m.UserInfo.LockoutEnabled)
</label>
</div>
<div class="form-check">
<label asp-for="@Model.UserInfo.TwoFactorEnabled" class="form-check-label">
<input asp-for="@Model.UserInfo.TwoFactorEnabled" class="form-check-input" />
@Html.DisplayNameFor(m => m.UserInfo.TwoFactorEnabled)
</label>
</div>
</div> </div>
<div class="tab-pane" id="CreateUser_RolesTab" role="tabpanel"> <div class="tab-pane" id="CreateUser_RolesTab" role="tabpanel">

6
src/Volo.Abp.Identity.Web/Pages/Identity/Users/CreateModal.cshtml.cs

@ -67,6 +67,12 @@ namespace Volo.Abp.Identity.Web.Pages.Identity.Users
[MaxLength(IdentityUserConsts.MaxPhoneNumberLength)] [MaxLength(IdentityUserConsts.MaxPhoneNumberLength)]
[Display(Name = "PhoneNumber")] [Display(Name = "PhoneNumber")]
public string PhoneNumber { get; set; } public string PhoneNumber { get; set; }
[Display(Name = "TwoFactorVerification")]
public bool TwoFactorEnabled { get; set; } = true;
[Display(Name = "AccountLockoutOnFailedLoginAttempts")]
public bool LockoutEnabled { get; set; } = true;
} }
public class AssignedRoleViewModel public class AssignedRoleViewModel

12
src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml

@ -45,6 +45,18 @@
<input asp-for="UserInfo.PhoneNumber" class="form-control" /> <input asp-for="UserInfo.PhoneNumber" class="form-control" />
<span asp-validation-for="UserInfo.PhoneNumber" class="text-danger"></span> <span asp-validation-for="UserInfo.PhoneNumber" class="text-danger"></span>
</div> </div>
<div class="form-check">
<label asp-for="@Model.UserInfo.LockoutEnabled" class="form-check-label">
<input asp-for="@Model.UserInfo.LockoutEnabled" class="form-check-input" />
@Html.DisplayNameFor(m => m.UserInfo.LockoutEnabled)
</label>
</div>
<div class="form-check">
<label asp-for="@Model.UserInfo.TwoFactorEnabled" class="form-check-label">
<input asp-for="@Model.UserInfo.TwoFactorEnabled" class="form-check-input" />
@Html.DisplayNameFor(m => m.UserInfo.TwoFactorEnabled)
</label>
</div>
</div> </div>
<div class="tab-pane" id="EditUser_RolesTab" role="tabpanel"> <div class="tab-pane" id="EditUser_RolesTab" role="tabpanel">

6
src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml.cs

@ -73,6 +73,12 @@ namespace Volo.Abp.Identity.Web.Pages.Identity.Users
[MaxLength(IdentityUserConsts.MaxPhoneNumberLength)] [MaxLength(IdentityUserConsts.MaxPhoneNumberLength)]
[Display(Name = "PhoneNumber")] [Display(Name = "PhoneNumber")]
public string PhoneNumber { get; set; } public string PhoneNumber { get; set; }
[Display(Name = "TwoFactorVerification")]
public bool TwoFactorEnabled { get; set; }
[Display(Name = "AccountLockoutOnFailedLoginAttempts")]
public bool LockoutEnabled { get; set; }
} }
public class AssignedRoleViewModel public class AssignedRoleViewModel

2
src/Volo.Abp.TestBase/AbpIntegratedTest.cs

@ -37,7 +37,7 @@ namespace Volo.Abp.TestBase
protected virtual void BeforeAddApplication(IServiceCollection services) protected virtual void BeforeAddApplication(IServiceCollection services)
{ {
} }
protected virtual void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) protected virtual void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)

73
test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AutoMapper_ConfigurationValidation_Tests.cs

@ -0,0 +1,73 @@
using AutoMapper;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Volo.Abp.Modularity;
using Volo.Abp.TestBase;
using Xunit;
using IObjectMapper = Volo.Abp.ObjectMapping.IObjectMapper;
namespace Volo.Abp.AutoMapper
{
public class AutoMapper_ConfigurationValidation_Tests : AbpIntegratedTest<AutoMapper_ConfigurationValidation_Tests.Module>
{
private readonly IObjectMapper _objectMapper;
public AutoMapper_ConfigurationValidation_Tests()
{
_objectMapper = ServiceProvider.GetRequiredService<IObjectMapper>();
}
[Fact]
public void Should_Validate_Configuration()
{
_objectMapper.Map<MySourceClass, MyClassValidated>(new MySourceClass {Value = "42"}).Value.ShouldBe("42");
_objectMapper.Map<MySourceClass, MyClassNonValidated>(new MySourceClass {Value = "42"}).ValueNotMatched.ShouldBe(null);
}
[DependsOn(typeof(AbpAutoMapperModule))]
public class Module : AbpModule
{
public override void ConfigureServices(IServiceCollection services)
{
services.Configure<AbpAutoMapperOptions>(options =>
{
options.UseStaticMapper = false;
options.AddProfile<ValidatedProfile>(true);
options.AddProfile<NonValidatedProfile>();
});
}
}
public class ValidatedProfile : Profile
{
public ValidatedProfile()
{
CreateMap<MySourceClass, MyClassValidated>();
}
}
public class NonValidatedProfile : Profile
{
public NonValidatedProfile()
{
CreateMap<MySourceClass, MyClassNonValidated>();
}
}
public class MySourceClass
{
public string Value { get; set; }
}
public class MyClassValidated
{
public string Value { get; set; }
}
public class MyClassNonValidated
{
public string ValueNotMatched { get; set; }
}
}
}

5
test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/AutofacTestModule.cs

@ -8,6 +8,11 @@ namespace Volo.Abp.AutoMapper
{ {
public override void ConfigureServices(IServiceCollection services) public override void ConfigureServices(IServiceCollection services)
{ {
services.Configure<AbpAutoMapperOptions>(options =>
{
options.UseStaticMapper = false;
});
services.AddAssemblyOf<AutoMapperTestModule>(); services.AddAssemblyOf<AutoMapperTestModule>();
} }
} }

Loading…
Cancel
Save