Skip to content

Commit f957c91

Browse files
authored
Merge branch 'main' into ac/pm-26429/add-validation-to-policy-data-and-metadata
2 parents 4433ba9 + ff4b3eb commit f957c91

27 files changed

+833
-127
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using Bit.Api.Utilities;
2+
3+
namespace Bit.Api.Billing.Attributes;
4+
5+
public class NonTokenizedPaymentMethodTypeValidationAttribute : StringMatchesAttribute
6+
{
7+
private static readonly string[] _acceptedValues = ["accountCredit"];
8+
9+
public NonTokenizedPaymentMethodTypeValidationAttribute() : base(_acceptedValues)
10+
{
11+
ErrorMessage = $"Payment method type must be one of: {string.Join(", ", _acceptedValues)}";
12+
}
13+
}

src/Api/Billing/Attributes/PaymentMethodTypeValidationAttribute.cs renamed to src/Api/Billing/Attributes/TokenizedPaymentMethodTypeValidationAttribute.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
namespace Bit.Api.Billing.Attributes;
44

5-
public class PaymentMethodTypeValidationAttribute : StringMatchesAttribute
5+
public class TokenizedPaymentMethodTypeValidationAttribute : StringMatchesAttribute
66
{
77
private static readonly string[] _acceptedValues = ["bankAccount", "card", "payPal"];
88

9-
public PaymentMethodTypeValidationAttribute() : base(_acceptedValues)
9+
public TokenizedPaymentMethodTypeValidationAttribute() : base(_acceptedValues)
1010
{
1111
ErrorMessage = $"Payment method type must be one of: {string.Join(", ", _acceptedValues)}";
1212
}

src/Api/Billing/Models/Requests/Payment/MinimalTokenizedPaymentMethodRequest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace Bit.Api.Billing.Models.Requests.Payment;
77
public class MinimalTokenizedPaymentMethodRequest
88
{
99
[Required]
10-
[PaymentMethodTypeValidation]
10+
[TokenizedPaymentMethodTypeValidation]
1111
public required string Type { get; set; }
1212

1313
[Required]
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System.ComponentModel.DataAnnotations;
2+
using Bit.Api.Billing.Attributes;
3+
using Bit.Core.Billing.Payment.Models;
4+
5+
namespace Bit.Api.Billing.Models.Requests.Payment;
6+
7+
public class NonTokenizedPaymentMethodRequest
8+
{
9+
[Required]
10+
[NonTokenizedPaymentMethodTypeValidation]
11+
public required string Type { get; set; }
12+
13+
public NonTokenizedPaymentMethod ToDomain()
14+
{
15+
return Type switch
16+
{
17+
"accountCredit" => new NonTokenizedPaymentMethod { Type = NonTokenizablePaymentMethodType.AccountCredit },
18+
_ => throw new InvalidOperationException($"Invalid value for {nameof(NonTokenizedPaymentMethod)}.{nameof(NonTokenizedPaymentMethod.Type)}")
19+
};
20+
}
21+
}

src/Api/Billing/Models/Requests/Premium/PremiumCloudHostedSubscriptionRequest.cs

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,49 @@
44

55
namespace Bit.Api.Billing.Models.Requests.Premium;
66

7-
public class PremiumCloudHostedSubscriptionRequest
7+
public class PremiumCloudHostedSubscriptionRequest : IValidatableObject
88
{
9-
[Required]
10-
public required MinimalTokenizedPaymentMethodRequest TokenizedPaymentMethod { get; set; }
9+
public MinimalTokenizedPaymentMethodRequest? TokenizedPaymentMethod { get; set; }
10+
public NonTokenizedPaymentMethodRequest? NonTokenizedPaymentMethod { get; set; }
1111

1212
[Required]
1313
public required MinimalBillingAddressRequest BillingAddress { get; set; }
1414

1515
[Range(0, 99)]
1616
public short AdditionalStorageGb { get; set; } = 0;
1717

18-
public (TokenizedPaymentMethod, BillingAddress, short) ToDomain()
18+
19+
public (PaymentMethod, BillingAddress, short) ToDomain()
1920
{
20-
var paymentMethod = TokenizedPaymentMethod.ToDomain();
21+
// Check if TokenizedPaymentMethod or NonTokenizedPaymentMethod is provided.
22+
var tokenizedPaymentMethod = TokenizedPaymentMethod?.ToDomain();
23+
var nonTokenizedPaymentMethod = NonTokenizedPaymentMethod?.ToDomain();
24+
25+
PaymentMethod paymentMethod = tokenizedPaymentMethod != null
26+
? tokenizedPaymentMethod
27+
: nonTokenizedPaymentMethod!;
28+
2129
var billingAddress = BillingAddress.ToDomain();
2230

2331
return (paymentMethod, billingAddress, AdditionalStorageGb);
2432
}
33+
34+
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
35+
{
36+
if (TokenizedPaymentMethod == null && NonTokenizedPaymentMethod == null)
37+
{
38+
yield return new ValidationResult(
39+
"Either TokenizedPaymentMethod or NonTokenizedPaymentMethod must be provided.",
40+
new[] { nameof(TokenizedPaymentMethod), nameof(NonTokenizedPaymentMethod) }
41+
);
42+
}
43+
44+
if (TokenizedPaymentMethod != null && NonTokenizedPaymentMethod != null)
45+
{
46+
yield return new ValidationResult(
47+
"Only one of TokenizedPaymentMethod or NonTokenizedPaymentMethod can be provided.",
48+
new[] { nameof(TokenizedPaymentMethod), nameof(NonTokenizedPaymentMethod) }
49+
);
50+
}
51+
}
2552
}

src/Core/AdminConsole/Models/Data/EventIntegrations/IIntegrationMessage.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ public interface IIntegrationMessage
66
{
77
IntegrationType IntegrationType { get; }
88
string MessageId { get; set; }
9+
string? OrganizationId { get; set; }
910
int RetryCount { get; }
1011
DateTime? DelayUntilDate { get; }
1112
void ApplyRetry(DateTime? handlerDelayUntilDate);

src/Core/AdminConsole/Models/Data/EventIntegrations/IntegrationMessage.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ public class IntegrationMessage : IIntegrationMessage
77
{
88
public IntegrationType IntegrationType { get; set; }
99
public required string MessageId { get; set; }
10+
public string? OrganizationId { get; set; }
1011
public required string RenderedTemplate { get; set; }
1112
public int RetryCount { get; set; } = 0;
1213
public DateTime? DelayUntilDate { get; set; }

src/Core/AdminConsole/Services/IEventIntegrationPublisher.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ namespace Bit.Core.Services;
55
public interface IEventIntegrationPublisher : IAsyncDisposable
66
{
77
Task PublishAsync(IIntegrationMessage message);
8-
Task PublishEventAsync(string body);
8+
Task PublishEventAsync(string body, string? organizationId);
99
}

src/Core/AdminConsole/Services/Implementations/EventIntegrations/AzureServiceBusService.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ public async Task PublishAsync(IIntegrationMessage message)
3030
var serviceBusMessage = new ServiceBusMessage(json)
3131
{
3232
Subject = message.IntegrationType.ToRoutingKey(),
33-
MessageId = message.MessageId
33+
MessageId = message.MessageId,
34+
PartitionKey = message.OrganizationId
3435
};
3536

3637
await _integrationSender.SendMessageAsync(serviceBusMessage);
@@ -44,18 +45,20 @@ public async Task PublishToRetryAsync(IIntegrationMessage message)
4445
{
4546
Subject = message.IntegrationType.ToRoutingKey(),
4647
ScheduledEnqueueTime = message.DelayUntilDate ?? DateTime.UtcNow,
47-
MessageId = message.MessageId
48+
MessageId = message.MessageId,
49+
PartitionKey = message.OrganizationId
4850
};
4951

5052
await _integrationSender.SendMessageAsync(serviceBusMessage);
5153
}
5254

53-
public async Task PublishEventAsync(string body)
55+
public async Task PublishEventAsync(string body, string? organizationId)
5456
{
5557
var message = new ServiceBusMessage(body)
5658
{
5759
ContentType = "application/json",
58-
MessageId = Guid.NewGuid().ToString()
60+
MessageId = Guid.NewGuid().ToString(),
61+
PartitionKey = organizationId
5962
};
6063

6164
await _eventSender.SendMessageAsync(message);

src/Core/AdminConsole/Services/Implementations/EventIntegrations/EventIntegrationEventWriteService.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,21 @@ public EventIntegrationEventWriteService(IEventIntegrationPublisher eventIntegra
1414
public async Task CreateAsync(IEvent e)
1515
{
1616
var body = JsonSerializer.Serialize(e);
17-
await _eventIntegrationPublisher.PublishEventAsync(body: body);
17+
await _eventIntegrationPublisher.PublishEventAsync(body: body, organizationId: e.OrganizationId?.ToString());
1818
}
1919

2020
public async Task CreateManyAsync(IEnumerable<IEvent> events)
2121
{
22-
var body = JsonSerializer.Serialize(events);
23-
await _eventIntegrationPublisher.PublishEventAsync(body: body);
24-
}
22+
var eventList = events as IList<IEvent> ?? events.ToList();
23+
if (eventList.Count == 0)
24+
{
25+
return;
26+
}
2527

28+
var organizationId = eventList[0].OrganizationId?.ToString();
29+
var body = JsonSerializer.Serialize(eventList);
30+
await _eventIntegrationPublisher.PublishEventAsync(body: body, organizationId: organizationId);
31+
}
2632
public async ValueTask DisposeAsync()
2733
{
2834
await _eventIntegrationPublisher.DisposeAsync();

0 commit comments

Comments
 (0)