1
0
mirror of https://github.com/bitwarden/server synced 2025-12-06 00:03:34 +00:00

feat(marketing-initiated-premium): (Auth) [PM-27540] Add optional Marketing Property to RegisterSendVerificationEmailRequestModel (#6598)

Adds an optional `FromMarketing` property to the RegisterSendVerificationEmailRequestModel.
This commit is contained in:
rr-bw
2025-11-21 09:38:59 -08:00
committed by GitHub
parent 62705917ab
commit 042279682a
5 changed files with 130 additions and 0 deletions

View File

@@ -0,0 +1,29 @@
using System.ComponentModel.DataAnnotations;
using Bit.Core.Auth.Models.Api.Request.Accounts;
namespace Bit.Core.Auth.Attributes;
public class MarketingInitiativeValidationAttribute : ValidationAttribute
{
private static readonly string[] _acceptedValues = [MarketingInitiativeConstants.Premium];
public MarketingInitiativeValidationAttribute()
{
ErrorMessage = $"Marketing initiative type must be one of: {string.Join(", ", _acceptedValues)}";
}
public override bool IsValid(object? value)
{
if (value == null)
{
return true;
}
if (value is not string str)
{
return false;
}
return _acceptedValues.Contains(str);
}
}

View File

@@ -0,0 +1,10 @@
namespace Bit.Core.Auth.Models.Api.Request.Accounts;
public static class MarketingInitiativeConstants
{
/// <summary>
/// Indicates that the user began the registration process on a marketing page designed
/// to streamline users who intend to setup a premium subscription after registration.
/// </summary>
public const string Premium = "premium";
}

View File

@@ -1,5 +1,6 @@
#nullable enable
using System.ComponentModel.DataAnnotations;
using Bit.Core.Auth.Attributes;
using Bit.Core.Utilities;
namespace Bit.Core.Auth.Models.Api.Request.Accounts;
@@ -11,4 +12,6 @@ public class RegisterSendVerificationEmailRequestModel
[StringLength(256)]
public required string Email { get; set; }
public bool ReceiveMarketingEmails { get; set; }
[MarketingInitiativeValidation]
public string? FromMarketing { get; set; }
}

View File

@@ -0,0 +1,70 @@
using Bit.Core.Auth.Attributes;
using Bit.Core.Auth.Models.Api.Request.Accounts;
using Xunit;
namespace Bit.Core.Test.Auth.Attributes;
public class MarketingInitiativeValidationAttributeTests
{
[Fact]
public void IsValid_NullValue_ReturnsTrue()
{
var sut = new MarketingInitiativeValidationAttribute();
var actual = sut.IsValid(null);
Assert.True(actual);
}
[Theory]
[InlineData(MarketingInitiativeConstants.Premium)]
public void IsValid_AcceptedValue_ReturnsTrue(string value)
{
var sut = new MarketingInitiativeValidationAttribute();
var actual = sut.IsValid(value);
Assert.True(actual);
}
[Theory]
[InlineData("invalid")]
[InlineData("")]
[InlineData("Premium")] // case sensitive - capitalized
[InlineData("PREMIUM")] // case sensitive - uppercase
[InlineData("premium ")] // trailing space
[InlineData(" premium")] // leading space
public void IsValid_InvalidStringValue_ReturnsFalse(string value)
{
var sut = new MarketingInitiativeValidationAttribute();
var actual = sut.IsValid(value);
Assert.False(actual);
}
[Theory]
[InlineData(123)] // integer
[InlineData(true)] // boolean
[InlineData(45.67)] // double
public void IsValid_NonStringValue_ReturnsFalse(object value)
{
var sut = new MarketingInitiativeValidationAttribute();
var actual = sut.IsValid(value);
Assert.False(actual);
}
[Fact]
public void ErrorMessage_ContainsAcceptedValues()
{
var sut = new MarketingInitiativeValidationAttribute();
var errorMessage = sut.ErrorMessage;
Assert.NotNull(errorMessage);
Assert.Contains("premium", errorMessage);
Assert.Contains("Marketing initiative type must be one of:", errorMessage);
}
}

View File

@@ -0,0 +1,18 @@
using Bit.Core.Auth.Models.Api.Request.Accounts;
using Xunit;
namespace Bit.Core.Test.Auth.Models.Api.Request.Accounts;
/// <summary>
/// Snapshot tests to ensure the string constants in <see cref="MarketingInitiativeConstants"/> do not change unintentionally.
/// If you intentionally change any of these values, please update the tests to reflect the new expected values.
/// </summary>
public class MarketingInitiativeConstantsSnapshotTests
{
[Fact]
public void MarketingInitiativeConstants_HaveCorrectValues()
{
// Assert
Assert.Equal("premium", MarketingInitiativeConstants.Premium);
}
}