Browse Source

Added tests for permission manager.

pull/208/head
Halil İbrahim Kalkan 9 years ago
parent
commit
075702507d
  1. 3
      src/Volo.Abp.Permissions/Volo/Abp/Permissions/PermissionManager.cs
  2. 2
      test/Volo.Abp.Permissions.Tests/Volo/Abp/Permissions/AbpPermissionTestModule.cs
  3. 2
      test/Volo.Abp.Permissions.Tests/Volo/Abp/Permissions/PermissionManager_Basic_Tests.cs
  4. 54
      test/Volo.Abp.Permissions.Tests/Volo/Abp/Permissions/PermissionManager_User_Tests.cs
  5. 4
      test/Volo.Abp.Permissions.Tests/Volo/Abp/Permissions/PermissionTestBase.cs
  6. 4
      test/Volo.Abp.Permissions.Tests/Volo/Abp/Permissions/PermissionTestDataBuilder.cs
  7. 2
      test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/AbpSettingsTestModule.cs
  8. 2
      test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/SettingManager_Basic_Tests.cs
  9. 46
      test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/SettingManager_User_Tests.cs
  10. 4
      test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/SettingTestDataBuilder.cs
  11. 4
      test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/SettingsTestBase.cs

3
src/Volo.Abp.Permissions/Volo/Abp/Permissions/PermissionManager.cs

@ -80,6 +80,9 @@ namespace Volo.Abp.Permissions
return false; return false;
} }
//TODO: Should also return for non-granted permissions!
//TODO: Create a method for getting only granted permissions!
public virtual async Task<List<PermissionGrantInfo>> GetAllAsync() public virtual async Task<List<PermissionGrantInfo>> GetAllAsync()
{ {
var permissionGrantInfos = new Dictionary<string, PermissionGrantInfo>(); var permissionGrantInfos = new Dictionary<string, PermissionGrantInfo>();

2
test/Volo.Abp.Permissions.Tests/Volo/Abp/Permissions/AbpPermissionTestModule.cs

@ -53,7 +53,7 @@ namespace Volo.Abp.Permissions
using (var scope = context.ServiceProvider.CreateScope()) using (var scope = context.ServiceProvider.CreateScope())
{ {
scope.ServiceProvider scope.ServiceProvider
.GetRequiredService<AbpPermissionTestDataBuilder>() .GetRequiredService<PermissionTestDataBuilder>()
.Build(); .Build();
} }
} }

2
test/Volo.Abp.Permissions.Tests/Volo/Abp/Permissions/PermissionManager_Basic_Tests.cs

@ -4,7 +4,7 @@ using Xunit;
namespace Volo.Abp.Permissions namespace Volo.Abp.Permissions
{ {
public class PermissionManager_Basic_Tests : AbpPermissionTestBase public class PermissionManager_Basic_Tests : PermissionTestBase
{ {
private readonly IPermissionManager _permissionManager; private readonly IPermissionManager _permissionManager;

54
test/Volo.Abp.Permissions.Tests/Volo/Abp/Permissions/PermissionManager_User_Tests.cs

@ -8,7 +8,7 @@ using Xunit;
namespace Volo.Abp.Permissions namespace Volo.Abp.Permissions
{ {
public class PermissionManager_User_Tests : AbpPermissionTestBase public class PermissionManager_User_Tests : PermissionTestBase
{ {
private readonly IPermissionManager _permissionManager; private readonly IPermissionManager _permissionManager;
@ -31,14 +31,14 @@ namespace Volo.Abp.Permissions
{ {
(await _permissionManager.IsGrantedForUserAsync( (await _permissionManager.IsGrantedForUserAsync(
"MyPermission1", "MyPermission1",
AbpPermissionTestDataBuilder.User1Id PermissionTestDataBuilder.User1Id
)).ShouldBeTrue(); )).ShouldBeTrue();
} }
[Fact] [Fact]
public async Task Should_Return_True_For_Granted_Current_User() public async Task Should_Return_True_For_Granted_Current_User()
{ {
_currentUserId = AbpPermissionTestDataBuilder.User1Id; _currentUserId = PermissionTestDataBuilder.User1Id;
(await _permissionManager.IsGrantedForCurrentUserAsync( (await _permissionManager.IsGrantedForCurrentUserAsync(
"MyPermission1" "MyPermission1"
@ -50,14 +50,14 @@ namespace Volo.Abp.Permissions
{ {
(await _permissionManager.IsGrantedForUserAsync( (await _permissionManager.IsGrantedForUserAsync(
"MyPermission1", "MyPermission1",
AbpPermissionTestDataBuilder.User2Id PermissionTestDataBuilder.User2Id
)).ShouldBeFalse(); )).ShouldBeFalse();
} }
[Fact] [Fact]
public async Task Should_Return_False_For_Non_Granted_Current_User() public async Task Should_Return_False_For_Non_Granted_Current_User()
{ {
_currentUserId = AbpPermissionTestDataBuilder.User2Id; _currentUserId = PermissionTestDataBuilder.User2Id;
(await _permissionManager.IsGrantedForCurrentUserAsync( (await _permissionManager.IsGrantedForCurrentUserAsync(
"MyPermission1" "MyPermission1"
@ -71,5 +71,49 @@ namespace Volo.Abp.Permissions
"MyPermission1" "MyPermission1"
)).ShouldBeFalse(); )).ShouldBeFalse();
} }
[Fact]
public async Task Should_Get_List_Of_Granted_Permissions_For_A_User()
{
//User1
var permission = await _permissionManager.GetAllForUserAsync(PermissionTestDataBuilder.User1Id);
permission.Count.ShouldBeGreaterThan(0);
permission.ShouldContain(p => p.Name == "MyPermission1" && p.IsGranted);
//User2
permission = await _permissionManager.GetAllForUserAsync(PermissionTestDataBuilder.User2Id);
permission.Count.ShouldBe(0);
}
[Fact]
public async Task Should_Get_List_Of_Granted_Permissions_For_Current_User()
{
//User1
_currentUserId = PermissionTestDataBuilder.User1Id;
var permission = await _permissionManager.GetAllForCurrentUserAsync();
permission.Count.ShouldBeGreaterThan(0);
permission.ShouldContain(p => p.Name == "MyPermission1" && p.IsGranted);
//User2
_currentUserId = PermissionTestDataBuilder.User2Id;
permission = await _permissionManager.GetAllForCurrentUserAsync();
permission.Count.ShouldBe(0);
}
[Fact]
public async Task Should_Grant_Permission_For_A_User()
{
(await _permissionManager.IsGrantedForUserAsync(
"MyPermission1",
PermissionTestDataBuilder.User2Id
)).ShouldBeFalse();
await _permissionManager.SetForUserAsync(PermissionTestDataBuilder.User2Id, "MyPermission1", true);
(await _permissionManager.IsGrantedForUserAsync(
"MyPermission1",
PermissionTestDataBuilder.User2Id
)).ShouldBeTrue();
}
} }
} }

4
test/Volo.Abp.Permissions.Tests/Volo/Abp/Permissions/AbpPermissionTestBase.cs → test/Volo.Abp.Permissions.Tests/Volo/Abp/Permissions/PermissionTestBase.cs

@ -7,7 +7,7 @@ using Volo.Abp.TestBase;
namespace Volo.Abp.Permissions namespace Volo.Abp.Permissions
{ {
public class AbpPermissionTestBase : AbpIntegratedTest<AbpPermissionTestModule> public class PermissionTestBase : AbpIntegratedTest<AbpPermissionTestModule>
{ {
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
{ {
@ -36,7 +36,7 @@ namespace Volo.Abp.Permissions
context.PermissionGrants.Where( context.PermissionGrants.Where(
s => s =>
s.ProviderName == UserSettingValueProvider.ProviderName && s.ProviderName == UserSettingValueProvider.ProviderName &&
s.ProviderKey == AbpPermissionTestDataBuilder.User1Id.ToString() && s.ProviderKey == PermissionTestDataBuilder.User1Id.ToString() &&
s.Name == "MySetting2" s.Name == "MySetting2"
).ToList() ).ToList()
); );

4
test/Volo.Abp.Permissions.Tests/Volo/Abp/Permissions/AbpPermissionTestDataBuilder.cs → test/Volo.Abp.Permissions.Tests/Volo/Abp/Permissions/PermissionTestDataBuilder.cs

@ -5,7 +5,7 @@ using Volo.Abp.Session;
namespace Volo.Abp.Permissions namespace Volo.Abp.Permissions
{ {
public class AbpPermissionTestDataBuilder : ITransientDependency public class PermissionTestDataBuilder : ITransientDependency
{ {
public static Guid User1Id { get; } = Guid.NewGuid(); public static Guid User1Id { get; } = Guid.NewGuid();
public static Guid User2Id { get; } = Guid.NewGuid(); public static Guid User2Id { get; } = Guid.NewGuid();
@ -13,7 +13,7 @@ namespace Volo.Abp.Permissions
private readonly IPermissionGrantRepository _permissionGrantRepository; private readonly IPermissionGrantRepository _permissionGrantRepository;
private readonly IGuidGenerator _guidGenerator; private readonly IGuidGenerator _guidGenerator;
public AbpPermissionTestDataBuilder(IGuidGenerator guidGenerator, IPermissionGrantRepository permissionGrantRepository) public PermissionTestDataBuilder(IGuidGenerator guidGenerator, IPermissionGrantRepository permissionGrantRepository)
{ {
_guidGenerator = guidGenerator; _guidGenerator = guidGenerator;
_permissionGrantRepository = permissionGrantRepository; _permissionGrantRepository = permissionGrantRepository;

2
test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/AbpSettingsTestModule.cs

@ -53,7 +53,7 @@ namespace Volo.Abp.Settings
using (var scope = context.ServiceProvider.CreateScope()) using (var scope = context.ServiceProvider.CreateScope())
{ {
scope.ServiceProvider scope.ServiceProvider
.GetRequiredService<AbpSettingTestDataBuilder>() .GetRequiredService<SettingTestDataBuilder>()
.Build(); .Build();
} }
} }

2
test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/SettingManager_Basic_Tests.cs

@ -4,7 +4,7 @@ using Xunit;
namespace Volo.Abp.Settings namespace Volo.Abp.Settings
{ {
public class SettingManager_Basic_Tests : AbpSettingsTestBase public class SettingManager_Basic_Tests : SettingsTestBase
{ {
private readonly ISettingManager _settingManager; private readonly ISettingManager _settingManager;

46
test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/SettingManager_User_Tests.cs

@ -9,7 +9,7 @@ using Xunit;
namespace Volo.Abp.Settings namespace Volo.Abp.Settings
{ {
public class SettingManager_User_Tests: AbpSettingsTestBase public class SettingManager_User_Tests: SettingsTestBase
{ {
private Guid? _currentUserId; private Guid? _currentUserId;
@ -30,8 +30,8 @@ namespace Volo.Abp.Settings
[Fact] [Fact]
public async Task Should_Get_From_Store_For_Given_User() public async Task Should_Get_From_Store_For_Given_User()
{ {
(await _settingManager.GetOrNullForUserAsync("MySetting2", AbpSettingTestDataBuilder.User1Id)).ShouldBe("user1-store-value"); (await _settingManager.GetOrNullForUserAsync("MySetting2", SettingTestDataBuilder.User1Id)).ShouldBe("user1-store-value");
(await _settingManager.GetOrNullForUserAsync("MySetting2", AbpSettingTestDataBuilder.User2Id)).ShouldBe("user2-store-value"); (await _settingManager.GetOrNullForUserAsync("MySetting2", SettingTestDataBuilder.User2Id)).ShouldBe("user2-store-value");
} }
[Fact] [Fact]
@ -49,10 +49,10 @@ namespace Volo.Abp.Settings
[Fact] [Fact]
public async Task Should_Get_From_Store_For_Current_User() public async Task Should_Get_From_Store_For_Current_User()
{ {
_currentUserId = AbpSettingTestDataBuilder.User1Id; _currentUserId = SettingTestDataBuilder.User1Id;
(await _settingManager.GetOrNullAsync("MySetting2")).ShouldBe("user1-store-value"); (await _settingManager.GetOrNullAsync("MySetting2")).ShouldBe("user1-store-value");
_currentUserId = AbpSettingTestDataBuilder.User2Id; _currentUserId = SettingTestDataBuilder.User2Id;
(await _settingManager.GetOrNullAsync("MySetting2")).ShouldBe("user2-store-value"); (await _settingManager.GetOrNullAsync("MySetting2")).ShouldBe("user2-store-value");
} }
@ -66,10 +66,10 @@ namespace Volo.Abp.Settings
[Fact] [Fact]
public async Task Should_Get_From_Store_For_Current_User_With_GetOrNullForCurrentUserAsync() public async Task Should_Get_From_Store_For_Current_User_With_GetOrNullForCurrentUserAsync()
{ {
_currentUserId = AbpSettingTestDataBuilder.User1Id; _currentUserId = SettingTestDataBuilder.User1Id;
(await _settingManager.GetOrNullForCurrentUserAsync("MySetting2")).ShouldBe("user1-store-value"); (await _settingManager.GetOrNullForCurrentUserAsync("MySetting2")).ShouldBe("user1-store-value");
_currentUserId = AbpSettingTestDataBuilder.User2Id; _currentUserId = SettingTestDataBuilder.User2Id;
(await _settingManager.GetOrNullForCurrentUserAsync("MySetting2")).ShouldBe("user2-store-value"); (await _settingManager.GetOrNullForCurrentUserAsync("MySetting2")).ShouldBe("user2-store-value");
} }
@ -90,7 +90,7 @@ namespace Volo.Abp.Settings
[Fact] [Fact]
public async Task Should_Get_All_From_Store_For_Given_User() public async Task Should_Get_All_From_Store_For_Given_User()
{ {
var settingValues = await _settingManager.GetAllForUserAsync(AbpSettingTestDataBuilder.User1Id); var settingValues = await _settingManager.GetAllForUserAsync(SettingTestDataBuilder.User1Id);
settingValues.ShouldContain(sv => sv.Name == "MySetting1" && sv.Value == "42"); settingValues.ShouldContain(sv => sv.Name == "MySetting1" && sv.Value == "42");
settingValues.ShouldContain(sv => sv.Name == "MySetting2" && sv.Value == "user1-store-value"); settingValues.ShouldContain(sv => sv.Name == "MySetting2" && sv.Value == "user1-store-value");
settingValues.ShouldContain(sv => sv.Name == "SettingNotSetInStore" && sv.Value == "default-value"); settingValues.ShouldContain(sv => sv.Name == "SettingNotSetInStore" && sv.Value == "default-value");
@ -99,7 +99,7 @@ namespace Volo.Abp.Settings
[Fact] [Fact]
public async Task Should_Get_All_From_Store_For_Given_User_Without_Fallback() public async Task Should_Get_All_From_Store_For_Given_User_Without_Fallback()
{ {
var settingValues = await _settingManager.GetAllForUserAsync(AbpSettingTestDataBuilder.User1Id, fallback: false); var settingValues = await _settingManager.GetAllForUserAsync(SettingTestDataBuilder.User1Id, fallback: false);
settingValues.ShouldContain(sv => sv.Name == "MySetting2" && sv.Value == "user1-store-value"); settingValues.ShouldContain(sv => sv.Name == "MySetting2" && sv.Value == "user1-store-value");
settingValues.ShouldContain(sv => sv.Name == "MySettingWithoutInherit" && sv.Value == "user1-store-value"); settingValues.ShouldContain(sv => sv.Name == "MySettingWithoutInherit" && sv.Value == "user1-store-value");
settingValues.ShouldNotContain(sv => sv.Name == "MySetting1"); settingValues.ShouldNotContain(sv => sv.Name == "MySetting1");
@ -108,11 +108,11 @@ namespace Volo.Abp.Settings
[Fact] [Fact]
public async Task Should_Delete_Setting_Record_When_Set_To_Null() public async Task Should_Delete_Setting_Record_When_Set_To_Null()
{ {
await _settingManager.SetForUserAsync(AbpSettingTestDataBuilder.User1Id, "MySetting2", null); await _settingManager.SetForUserAsync(SettingTestDataBuilder.User1Id, "MySetting2", null);
GetSettingsFromDbContext( GetSettingsFromDbContext(
UserSettingValueProvider.ProviderName, UserSettingValueProvider.ProviderName,
AbpSettingTestDataBuilder.User1Id.ToString(), SettingTestDataBuilder.User1Id.ToString(),
"MySetting2" "MySetting2"
).Count.ShouldBe(0); ).Count.ShouldBe(0);
} }
@ -120,17 +120,17 @@ namespace Volo.Abp.Settings
[Fact] [Fact]
public async Task Should_Change_User_Setting() public async Task Should_Change_User_Setting()
{ {
(await _settingManager.GetOrNullForUserAsync("MySetting2", AbpSettingTestDataBuilder.User1Id)) (await _settingManager.GetOrNullForUserAsync("MySetting2", SettingTestDataBuilder.User1Id))
.ShouldBe("user1-store-value"); .ShouldBe("user1-store-value");
await _settingManager.SetForUserAsync(AbpSettingTestDataBuilder.User1Id, "MySetting2", "user1-new-store-value"); await _settingManager.SetForUserAsync(SettingTestDataBuilder.User1Id, "MySetting2", "user1-new-store-value");
(await _settingManager.GetOrNullForUserAsync("MySetting2", AbpSettingTestDataBuilder.User1Id)) (await _settingManager.GetOrNullForUserAsync("MySetting2", SettingTestDataBuilder.User1Id))
.ShouldBe("user1-new-store-value"); .ShouldBe("user1-new-store-value");
GetSettingsFromDbContext( GetSettingsFromDbContext(
UserSettingValueProvider.ProviderName, UserSettingValueProvider.ProviderName,
AbpSettingTestDataBuilder.User1Id.ToString(), SettingTestDataBuilder.User1Id.ToString(),
"MySetting2" "MySetting2"
).Single().Value.ShouldBe("user1-new-store-value"); ).Single().Value.ShouldBe("user1-new-store-value");
} }
@ -139,18 +139,18 @@ namespace Volo.Abp.Settings
public async Task Should_Delete_Setting_Record_When_Set_To_Fallback_Value() public async Task Should_Delete_Setting_Record_When_Set_To_Fallback_Value()
{ {
await _settingManager.SetForUserAsync( await _settingManager.SetForUserAsync(
AbpSettingTestDataBuilder.User1Id, SettingTestDataBuilder.User1Id,
"MySetting2", "MySetting2",
"default-store-value" "default-store-value"
); );
GetSettingsFromDbContext( GetSettingsFromDbContext(
UserSettingValueProvider.ProviderName, UserSettingValueProvider.ProviderName,
AbpSettingTestDataBuilder.User1Id.ToString(), SettingTestDataBuilder.User1Id.ToString(),
"MySetting2" "MySetting2"
).Count.ShouldBe(0); ).Count.ShouldBe(0);
(await _settingManager.GetOrNullForUserAsync("MySetting2", AbpSettingTestDataBuilder.User1Id)) (await _settingManager.GetOrNullForUserAsync("MySetting2", SettingTestDataBuilder.User1Id))
.ShouldBe("default-store-value"); .ShouldBe("default-store-value");
} }
@ -158,7 +158,7 @@ namespace Volo.Abp.Settings
public async Task Should_Not_Delete_Setting_Record_When_Set_To_Fallback_Value_If_Forced() public async Task Should_Not_Delete_Setting_Record_When_Set_To_Fallback_Value_If_Forced()
{ {
await _settingManager.SetForUserAsync( await _settingManager.SetForUserAsync(
AbpSettingTestDataBuilder.User1Id, SettingTestDataBuilder.User1Id,
"MySetting2", "MySetting2",
"default-store-value", "default-store-value",
forceToSet: true forceToSet: true
@ -166,19 +166,19 @@ namespace Volo.Abp.Settings
GetSettingsFromDbContext( GetSettingsFromDbContext(
UserSettingValueProvider.ProviderName, UserSettingValueProvider.ProviderName,
AbpSettingTestDataBuilder.User1Id.ToString(), SettingTestDataBuilder.User1Id.ToString(),
"MySetting2" "MySetting2"
).Single().Value.ShouldBe("default-store-value"); ).Single().Value.ShouldBe("default-store-value");
(await _settingManager.GetOrNullForUserAsync("MySetting2", AbpSettingTestDataBuilder.User1Id)) (await _settingManager.GetOrNullForUserAsync("MySetting2", SettingTestDataBuilder.User1Id))
.ShouldBe("default-store-value"); .ShouldBe("default-store-value");
} }
[Fact] [Fact]
public async Task Should_Get_For_Given_User_For_Non_Inherited_Setting() public async Task Should_Get_For_Given_User_For_Non_Inherited_Setting()
{ {
(await _settingManager.GetOrNullForUserAsync("MySettingWithoutInherit", AbpSettingTestDataBuilder.User1Id)).ShouldBe("user1-store-value"); (await _settingManager.GetOrNullForUserAsync("MySettingWithoutInherit", SettingTestDataBuilder.User1Id)).ShouldBe("user1-store-value");
(await _settingManager.GetOrNullForUserAsync("MySettingWithoutInherit", AbpSettingTestDataBuilder.User2Id)).ShouldBeNull(); //Does not inherit! (await _settingManager.GetOrNullForUserAsync("MySettingWithoutInherit", SettingTestDataBuilder.User2Id)).ShouldBeNull(); //Does not inherit!
(await _settingManager.GetOrNullGlobalAsync("MySettingWithoutInherit")).ShouldBe("default-store-value"); (await _settingManager.GetOrNullGlobalAsync("MySettingWithoutInherit")).ShouldBe("default-store-value");
} }
} }

4
test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/AbpSettingTestDataBuilder.cs → test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/SettingTestDataBuilder.cs

@ -5,7 +5,7 @@ using Volo.Abp.Session;
namespace Volo.Abp.Settings namespace Volo.Abp.Settings
{ {
public class AbpSettingTestDataBuilder : ITransientDependency public class SettingTestDataBuilder : ITransientDependency
{ {
public static Guid User1Id { get; } = Guid.NewGuid(); public static Guid User1Id { get; } = Guid.NewGuid();
public static Guid User2Id { get; } = Guid.NewGuid(); public static Guid User2Id { get; } = Guid.NewGuid();
@ -13,7 +13,7 @@ namespace Volo.Abp.Settings
private readonly ISettingRepository _settingRepository; private readonly ISettingRepository _settingRepository;
private readonly IGuidGenerator _guidGenerator; private readonly IGuidGenerator _guidGenerator;
public AbpSettingTestDataBuilder(ISettingRepository settingRepository, IGuidGenerator guidGenerator) public SettingTestDataBuilder(ISettingRepository settingRepository, IGuidGenerator guidGenerator)
{ {
_settingRepository = settingRepository; _settingRepository = settingRepository;
_guidGenerator = guidGenerator; _guidGenerator = guidGenerator;

4
test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/AbpSettingsTestBase.cs → test/Volo.Abp.Settings.Tests/Volo/Abp/Settings/SettingsTestBase.cs

@ -7,7 +7,7 @@ using Volo.Abp.TestBase;
namespace Volo.Abp.Settings namespace Volo.Abp.Settings
{ {
public class AbpSettingsTestBase : AbpIntegratedTest<AbpSettingsTestModule> public class SettingsTestBase : AbpIntegratedTest<AbpSettingsTestModule>
{ {
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
{ {
@ -36,7 +36,7 @@ namespace Volo.Abp.Settings
context.Settings.Where( context.Settings.Where(
s => s =>
s.ProviderName == UserSettingValueProvider.ProviderName && s.ProviderName == UserSettingValueProvider.ProviderName &&
s.ProviderKey == AbpSettingTestDataBuilder.User1Id.ToString() && s.ProviderKey == SettingTestDataBuilder.User1Id.ToString() &&
s.Name == "MySetting2" s.Name == "MySetting2"
).ToList() ).ToList()
); );
Loading…
Cancel
Save