Browse Source

Fix ExtraProperties filter bypass in Mapperly single-parameter Map

pull/25486/head
maliming 4 months ago
parent
commit
61ed99b418
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 4
      framework/src/Volo.Abp.Mapperly/Volo/Abp/Mapperly/MapperlyAutoObjectMappingProvider.cs
  2. 35
      framework/test/Volo.Abp.Mapperly.Tests/Mapperly/AbpAutoMapperExtensibleDtoExtensions_Tests.cs
  3. 16
      framework/test/Volo.Abp.Mapperly.Tests/Volo/Abp/Mapperly/AbpReverseMapperly_Tests.cs
  4. 22
      framework/test/Volo.Abp.Mapperly.Tests/Volo/Abp/Mapperly/ExtraProperties_Dictionary_Reference_Tests.cs
  5. 16
      framework/test/Volo.Abp.Mapperly.Tests/Volo/Abp/Mapperly/MapperlyTestModule.cs
  6. 23
      framework/test/Volo.Abp.Mapperly.Tests/Volo/Abp/Mapperly/SampleClasses/MapperlyMappers.cs

4
framework/src/Volo.Abp.Mapperly/Volo/Abp/Mapperly/MapperlyAutoObjectMappingProvider.cs

@ -46,7 +46,7 @@ public class MapperlyAutoObjectMappingProvider : IAutoObjectMappingProvider
{
mapper.BeforeMap((TSource)source);
var destination = mapper.Map((TSource)source);
TryMapExtraProperties(mapper.GetType().GetSingleAttributeOrNull<MapExtraPropertiesAttribute>(), (TSource)source, destination, GetExtraProperties(destination));
TryMapExtraProperties(mapper.GetType().GetSingleAttributeOrNull<MapExtraPropertiesAttribute>(), (TSource)source, destination, new ExtraPropertyDictionary());
mapper.AfterMap((TSource)source, destination);
return destination;
}
@ -56,7 +56,7 @@ public class MapperlyAutoObjectMappingProvider : IAutoObjectMappingProvider
{
reverseMapper.BeforeReverseMap((TSource)source);
var destination = reverseMapper.ReverseMap((TSource)source);
TryMapExtraProperties(reverseMapper.GetType().GetSingleAttributeOrNull<MapExtraPropertiesAttribute>(), (TSource)source, destination, GetExtraProperties(destination));
TryMapExtraProperties(reverseMapper.GetType().GetSingleAttributeOrNull<MapExtraPropertiesAttribute>(), (TSource)source, destination, new ExtraPropertyDictionary());
reverseMapper.AfterReverseMap((TSource)source, destination);
return destination;
}

35
framework/test/Volo.Abp.Mapperly.Tests/Mapperly/AbpAutoMapperExtensibleDtoExtensions_Tests.cs

@ -40,6 +40,25 @@ public class AbpAutoMapperExtensibleDtoExtensions_Tests : AbpIntegratedTest<Mapp
personDto.HasProperty("Sex").ShouldBeFalse(); //Not defined in both classes
}
[Fact]
public void MapExtraPropertiesTo_Should_Only_Map_Defined_Properties_By_Default_With_Single_Parameter_Map()
{
var person = new ExtensibleTestPerson()
.SetProperty("Name", "John")
.SetProperty("Age", 42)
.SetProperty("ChildCount", 2)
.SetProperty("Sex", "male")
.SetProperty("CityName", "Adana");
var personDto = _objectMapper.Map<ExtensibleTestPerson, ExtensibleTestPersonDto>(person);
personDto.GetProperty<string>("Name").ShouldBe("John"); //Defined in both classes
personDto.GetProperty<int>("ChildCount").ShouldBe(0); //Not defined in the source, but was set to the default value by ExtensibleTestPersonDto constructor
personDto.GetProperty("CityName").ShouldBeNull(); //Ignored, but was set to the default value by ExtensibleTestPersonDto constructor
personDto.HasProperty("Age").ShouldBeFalse(); //Not defined on the destination
personDto.HasProperty("Sex").ShouldBeFalse(); //Not defined in both classes
}
[Fact]
public void MapExtraProperties_Also_Should_Map_To_RegularProperties()
{
@ -65,6 +84,22 @@ public class AbpAutoMapperExtensibleDtoExtensions_Tests : AbpIntegratedTest<Mapp
personDto.IsActive.ShouldBe(true);
}
[Fact]
public void MapExtraProperties_Also_Should_Map_To_RegularProperties_With_Single_Parameter_Map()
{
var person = new ExtensibleTestPerson()
.SetProperty("Name", "John")
.SetProperty("Age", 42);
var personDto = _objectMapper.Map<ExtensibleTestPerson, ExtensibleTestPersonWithRegularPropertiesDto>(person);
personDto.HasProperty("Name").ShouldBe(false);
personDto.Name.ShouldBe("John");
personDto.HasProperty("Age").ShouldBe(false);
personDto.Age.ShouldBe(42);
}
[Fact(Skip = "Mapperly requires IHasExtraProperties.ExtraPropertyDictionary to be marked as nullable")]
public void MapExtraPropertiesTo_Should_Ignored_If_ExtraProperties_Is_Null()
{

16
framework/test/Volo.Abp.Mapperly.Tests/Volo/Abp/Mapperly/AbpReverseMapperly_Tests.cs

@ -1,6 +1,9 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Riok.Mapperly.Abstractions;
using Shouldly;
using Volo.Abp.Data;
using Volo.Abp.Mapperly.SampleClasses;
using Volo.Abp.ObjectMapping;
using Volo.Abp.Testing;
using Xunit;
@ -83,4 +86,17 @@ public class AbpReverseMapperly_Tests : AbpIntegratedTest<MapperlyTestModule>
myClass.Id.ShouldBe("2");
myClass.Name.ShouldBe("BeforeReverseMap Test2 AfterReverseMap");
}
[Fact]
public void MapExtraProperties_Should_Filter_With_Single_Parameter_ReverseMap()
{
var dto = new ExtensibleReverseDto { Id = Guid.NewGuid() }
.SetProperty("Tag", "ok")
.SetProperty("Secret", "leaked");
var entity = _objectMapper.Map<ExtensibleReverseDto, ExtensibleReverseEntity>(dto);
entity.GetProperty<string>("Tag").ShouldBe("ok");
entity.HasProperty("Secret").ShouldBeFalse();
}
}

22
framework/test/Volo.Abp.Mapperly.Tests/Volo/Abp/Mapperly/ExtraProperties_Dictionary_Reference_Tests.cs

@ -101,6 +101,28 @@ public class ExtraProperties_Dictionary_Reference_Tests : AbpIntegratedTest<Mapp
destination.ExtraProperties["DestinationProperty"].ShouldBe("DestinationValue");
}
[Fact]
public void Should_Not_Share_ExtraProperties_Reference_With_Source_When_Using_Single_Parameter_Map()
{
var source = new TestEntityWithExtraProperties
{
Id = Guid.NewGuid(),
Name = "Source Entity"
};
source.SetProperty("TestProperty", "TestValue");
source.SetProperty("NumberProperty", 42);
var originalSourceReference = source.ExtraProperties;
var destination = _objectMapper.Map<TestEntityWithExtraProperties, TestEntityDtoWithExtraProperties>(source);
ReferenceEquals(source.ExtraProperties, originalSourceReference).ShouldBeTrue();
ReferenceEquals(source.ExtraProperties, destination.ExtraProperties).ShouldBeFalse();
destination.ExtraProperties["TestProperty"].ShouldBe("TestValue");
destination.ExtraProperties["NumberProperty"].ShouldBe(42);
}
[Fact]
public void Should_Handle_Readonly_ExtraProperties_Gracefully()
{

16
framework/test/Volo.Abp.Mapperly.Tests/Volo/Abp/Mapperly/MapperlyTestModule.cs

@ -1,5 +1,7 @@
using Volo.Abp.Modularity;
using Volo.Abp.Mapperly.SampleClasses;
using Volo.Abp.Modularity;
using Volo.Abp.ObjectExtending;
using Volo.Abp.Threading;
namespace Volo.Abp.Mapperly;
@ -9,5 +11,17 @@ namespace Volo.Abp.Mapperly;
)]
public class MapperlyTestModule : AbpModule
{
private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner();
public override void PreConfigureServices(ServiceConfigurationContext context)
{
OneTimeRunner.Run(() =>
{
ObjectExtensionManager.Instance
.AddOrUpdateProperty<ExtensibleReverseEntity, string>("Tag")
.AddOrUpdateProperty<ExtensibleReverseEntity, string>("Secret")
.AddOrUpdateProperty<ExtensibleReverseDto, string>("Tag")
.AddOrUpdateProperty<ExtensibleReverseDto, string>("Secret");
});
}
}

23
framework/test/Volo.Abp.Mapperly.Tests/Volo/Abp/Mapperly/SampleClasses/MapperlyMappers.cs

@ -82,4 +82,27 @@ public partial class TestEntityWithReadonlyExtraPropertiesMapper : MapperBase<Te
public override partial TestEntityWithReadonlyExtraProperties Map(TestEntityWithReadonlyExtraProperties source);
public override partial void Map(TestEntityWithReadonlyExtraProperties source, TestEntityWithReadonlyExtraProperties destination);
}
public class ExtensibleReverseEntity : ExtensibleObject
{
public Guid Id { get; set; }
}
public class ExtensibleReverseDto : ExtensibleObject
{
public Guid Id { get; set; }
}
[Mapper]
[MapExtraProperties(IgnoredProperties = ["Secret"])]
public partial class ExtensibleReverseMapper : TwoWayMapperBase<ExtensibleReverseEntity, ExtensibleReverseDto>
{
public override partial ExtensibleReverseDto Map(ExtensibleReverseEntity source);
public override partial void Map(ExtensibleReverseEntity source, ExtensibleReverseDto destination);
public override partial ExtensibleReverseEntity ReverseMap(ExtensibleReverseDto destination);
public override partial void ReverseMap(ExtensibleReverseDto destination, ExtensibleReverseEntity source);
}
Loading…
Cancel
Save