Skip to content

Commit 0422796

Browse files
authored
feat(marketing-initiated-premium): (Auth) [PM-27540] Add optional Marketing Property to RegisterSendVerificationEmailRequestModel (#6598)
Adds an optional `FromMarketing` property to the RegisterSendVerificationEmailRequestModel.
1 parent 6270591 commit 0422796

File tree

5 files changed

+130
-0
lines changed

5 files changed

+130
-0
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using System.ComponentModel.DataAnnotations;
2+
using Bit.Core.Auth.Models.Api.Request.Accounts;
3+
4+
namespace Bit.Core.Auth.Attributes;
5+
6+
public class MarketingInitiativeValidationAttribute : ValidationAttribute
7+
{
8+
private static readonly string[] _acceptedValues = [MarketingInitiativeConstants.Premium];
9+
10+
public MarketingInitiativeValidationAttribute()
11+
{
12+
ErrorMessage = $"Marketing initiative type must be one of: {string.Join(", ", _acceptedValues)}";
13+
}
14+
15+
public override bool IsValid(object? value)
16+
{
17+
if (value == null)
18+
{
19+
return true;
20+
}
21+
22+
if (value is not string str)
23+
{
24+
return false;
25+
}
26+
27+
return _acceptedValues.Contains(str);
28+
}
29+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace Bit.Core.Auth.Models.Api.Request.Accounts;
2+
3+
public static class MarketingInitiativeConstants
4+
{
5+
/// <summary>
6+
/// Indicates that the user began the registration process on a marketing page designed
7+
/// to streamline users who intend to setup a premium subscription after registration.
8+
/// </summary>
9+
public const string Premium = "premium";
10+
}

src/Core/Auth/Models/Api/Request/Accounts/RegisterSendVerificationEmailRequestModel.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#nullable enable
22
using System.ComponentModel.DataAnnotations;
3+
using Bit.Core.Auth.Attributes;
34
using Bit.Core.Utilities;
45

56
namespace Bit.Core.Auth.Models.Api.Request.Accounts;
@@ -11,4 +12,6 @@ public class RegisterSendVerificationEmailRequestModel
1112
[StringLength(256)]
1213
public required string Email { get; set; }
1314
public bool ReceiveMarketingEmails { get; set; }
15+
[MarketingInitiativeValidation]
16+
public string? FromMarketing { get; set; }
1417
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
using Bit.Core.Auth.Attributes;
2+
using Bit.Core.Auth.Models.Api.Request.Accounts;
3+
using Xunit;
4+
5+
namespace Bit.Core.Test.Auth.Attributes;
6+
7+
public class MarketingInitiativeValidationAttributeTests
8+
{
9+
[Fact]
10+
public void IsValid_NullValue_ReturnsTrue()
11+
{
12+
var sut = new MarketingInitiativeValidationAttribute();
13+
14+
var actual = sut.IsValid(null);
15+
16+
Assert.True(actual);
17+
}
18+
19+
[Theory]
20+
[InlineData(MarketingInitiativeConstants.Premium)]
21+
public void IsValid_AcceptedValue_ReturnsTrue(string value)
22+
{
23+
var sut = new MarketingInitiativeValidationAttribute();
24+
25+
var actual = sut.IsValid(value);
26+
27+
Assert.True(actual);
28+
}
29+
30+
[Theory]
31+
[InlineData("invalid")]
32+
[InlineData("")]
33+
[InlineData("Premium")] // case sensitive - capitalized
34+
[InlineData("PREMIUM")] // case sensitive - uppercase
35+
[InlineData("premium ")] // trailing space
36+
[InlineData(" premium")] // leading space
37+
public void IsValid_InvalidStringValue_ReturnsFalse(string value)
38+
{
39+
var sut = new MarketingInitiativeValidationAttribute();
40+
41+
var actual = sut.IsValid(value);
42+
43+
Assert.False(actual);
44+
}
45+
46+
[Theory]
47+
[InlineData(123)] // integer
48+
[InlineData(true)] // boolean
49+
[InlineData(45.67)] // double
50+
public void IsValid_NonStringValue_ReturnsFalse(object value)
51+
{
52+
var sut = new MarketingInitiativeValidationAttribute();
53+
54+
var actual = sut.IsValid(value);
55+
56+
Assert.False(actual);
57+
}
58+
59+
[Fact]
60+
public void ErrorMessage_ContainsAcceptedValues()
61+
{
62+
var sut = new MarketingInitiativeValidationAttribute();
63+
64+
var errorMessage = sut.ErrorMessage;
65+
66+
Assert.NotNull(errorMessage);
67+
Assert.Contains("premium", errorMessage);
68+
Assert.Contains("Marketing initiative type must be one of:", errorMessage);
69+
}
70+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using Bit.Core.Auth.Models.Api.Request.Accounts;
2+
using Xunit;
3+
4+
namespace Bit.Core.Test.Auth.Models.Api.Request.Accounts;
5+
6+
/// <summary>
7+
/// Snapshot tests to ensure the string constants in <see cref="MarketingInitiativeConstants"/> do not change unintentionally.
8+
/// If you intentionally change any of these values, please update the tests to reflect the new expected values.
9+
/// </summary>
10+
public class MarketingInitiativeConstantsSnapshotTests
11+
{
12+
[Fact]
13+
public void MarketingInitiativeConstants_HaveCorrectValues()
14+
{
15+
// Assert
16+
Assert.Equal("premium", MarketingInitiativeConstants.Premium);
17+
}
18+
}

0 commit comments

Comments
 (0)