Skip to content

Commit 2ce9827

Browse files
chore: [PM-29055] remove pm-25379-use-new-organization-metadata-structure feature flag (#6966)
Remove the fully-released feature flag and clean up the old code path: - Remove flag constant from FeatureFlagKeys - Remove [RequireFeature] gate from VNext billing controllers - Remove old GetMetadataAsync endpoint from OrganizationBillingController - Remove GetMetadata from IOrganizationBillingService and implementation - Remove IsOnSecretsManagerStandalone private helper - Remove associated tests
1 parent 8fefae9 commit 2ce9827

8 files changed

Lines changed: 0 additions & 260 deletions

File tree

src/Api/Billing/Controllers/OrganizationBillingController.cs

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,25 +21,6 @@ public class OrganizationBillingController(
2121
IStripePaymentService paymentService,
2222
IPaymentHistoryService paymentHistoryService) : BaseBillingController
2323
{
24-
// TODO: Remove when pm-25379-use-new-organization-metadata-structure is removed.
25-
[HttpGet("metadata")]
26-
public async Task<IResult> GetMetadataAsync([FromRoute] Guid organizationId)
27-
{
28-
if (!await currentContext.OrganizationUser(organizationId))
29-
{
30-
return Error.Unauthorized();
31-
}
32-
33-
var metadata = await organizationBillingService.GetMetadata(organizationId);
34-
35-
if (metadata == null)
36-
{
37-
return Error.NotFound();
38-
}
39-
40-
return TypedResults.Ok(metadata);
41-
}
42-
4324
// TODO: Migrate to Query / OrganizationBillingVNextController
4425
[HttpGet("history")]
4526
public async Task<IResult> GetHistoryAsync([FromRoute] Guid organizationId)

src/Api/Billing/Controllers/VNext/OrganizationBillingVNextController.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using Bit.Api.Billing.Models.Requests.Payment;
55
using Bit.Api.Billing.Models.Requests.Subscriptions;
66
using Bit.Api.Billing.Models.Requirements;
7-
using Bit.Core;
87
using Bit.Core.AdminConsole.Entities;
98
using Bit.Core.Billing.Commands;
109
using Bit.Core.Billing.Organizations.Queries;
@@ -117,7 +116,6 @@ public async Task<IResult> RestartSubscriptionAsync(
117116

118117
[Authorize<MemberOrProviderRequirement>]
119118
[HttpGet("metadata")]
120-
[RequireFeature(FeatureFlagKeys.PM25379_UseNewOrganizationMetadataStructure)]
121119
[InjectOrganization]
122120
public async Task<IResult> GetMetadataAsync(
123121
[BindNever] Organization organization)

src/Api/Billing/Controllers/VNext/SelfHostedOrganizationBillingVNextController.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using Bit.Api.AdminConsole.Authorization;
22
using Bit.Api.AdminConsole.Authorization.Requirements;
33
using Bit.Api.Billing.Attributes;
4-
using Bit.Core;
54
using Bit.Core.AdminConsole.Entities;
65
using Bit.Core.Billing.Organizations.Queries;
76
using Bit.Core.Utilities;
@@ -19,7 +18,6 @@ public class SelfHostedOrganizationBillingVNextController(
1918
{
2019
[Authorize<MemberOrProviderRequirement>]
2120
[HttpGet("metadata")]
22-
[RequireFeature(FeatureFlagKeys.PM25379_UseNewOrganizationMetadataStructure)]
2321
[InjectOrganization]
2422
public async Task<IResult> GetMetadataAsync([BindNever] Organization organization)
2523
{

src/Core/Billing/Organizations/Services/IOrganizationBillingService.cs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,6 @@ public interface IOrganizationBillingService
2626
/// </example>
2727
Task Finalize(OrganizationSale sale);
2828

29-
/// <summary>
30-
/// Retrieve metadata about the organization represented bsy the provided <paramref name="organizationId"/>.
31-
/// </summary>
32-
/// <param name="organizationId">The ID of the organization to retrieve metadata for.</param>
33-
/// <returns>An <see cref="OrganizationMetadata"/> record.</returns>
34-
Task<OrganizationMetadata?> GetMetadata(Guid organizationId);
35-
3629
/// <summary>
3730
/// Updates the provided <paramref name="organization"/>'s payment source and tax information.
3831
/// If the <paramref name="organization"/> does not have a Stripe <see cref="Stripe.Customer"/>, this method will create one using the provided

src/Core/Billing/Organizations/Services/OrganizationBillingService.cs

Lines changed: 0 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -54,52 +54,6 @@ public async Task Finalize(OrganizationSale sale)
5454
}
5555
}
5656

57-
public async Task<OrganizationMetadata?> GetMetadata(Guid organizationId)
58-
{
59-
var organization = await organizationRepository.GetByIdAsync(organizationId);
60-
61-
if (organization == null)
62-
{
63-
return null;
64-
}
65-
66-
if (globalSettings.SelfHosted)
67-
{
68-
return OrganizationMetadata.Default;
69-
}
70-
71-
var orgOccupiedSeats = await organizationRepository.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id);
72-
73-
if (string.IsNullOrWhiteSpace(organization.GatewaySubscriptionId))
74-
{
75-
return OrganizationMetadata.Default with
76-
{
77-
OrganizationOccupiedSeats = orgOccupiedSeats.Total
78-
};
79-
}
80-
81-
var customer = await subscriberService.GetCustomer(organization);
82-
83-
var subscription = await subscriberService.GetSubscription(organization, new SubscriptionGetOptions
84-
{
85-
Expand = ["discounts.coupon.applies_to"]
86-
});
87-
88-
if (customer == null || subscription == null)
89-
{
90-
return OrganizationMetadata.Default with
91-
{
92-
OrganizationOccupiedSeats = orgOccupiedSeats.Total
93-
};
94-
}
95-
96-
var isOnSecretsManagerStandalone = await IsOnSecretsManagerStandalone(organization, customer, subscription);
97-
98-
return new OrganizationMetadata(
99-
isOnSecretsManagerStandalone,
100-
orgOccupiedSeats.Total);
101-
}
102-
10357
public async Task UpdatePaymentMethod(
10458
Organization organization,
10559
TokenizedPaymentSource tokenizedPaymentSource,
@@ -565,38 +519,6 @@ ProductTierType.TeamsStarter or
565519
return customer;
566520
}
567521

568-
private async Task<bool> IsOnSecretsManagerStandalone(
569-
Organization organization,
570-
Customer? customer,
571-
Subscription? subscription)
572-
{
573-
if (customer == null || subscription == null)
574-
{
575-
return false;
576-
}
577-
578-
var plan = await pricingClient.GetPlanOrThrow(organization.PlanType);
579-
580-
if (!plan.SupportsSecretsManager)
581-
{
582-
return false;
583-
}
584-
585-
var coupon = subscription.Discounts?.FirstOrDefault(discount =>
586-
discount.Coupon?.Id == StripeConstants.CouponIDs.SecretsManagerStandalone)?.Coupon;
587-
588-
if (coupon == null)
589-
{
590-
return false;
591-
}
592-
593-
var subscriptionProductIds = subscription.Items.Data.Select(item => item.Plan.ProductId);
594-
595-
var couponAppliesTo = coupon.AppliesTo?.Products;
596-
597-
return subscriptionProductIds.Intersect(couponAppliesTo ?? []).Any();
598-
}
599-
600522
private async Task UpdateMissingPaymentMethodBehaviourAsync(Organization organization)
601523
{
602524
var subscription = await subscriberService.GetSubscriptionOrThrow(organization);

src/Core/Constants.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,6 @@ public static class FeatureFlagKeys
182182

183183
/* Billing Team */
184184
public const string TrialPayment = "PM-8163-trial-payment";
185-
public const string PM25379_UseNewOrganizationMetadataStructure = "pm-25379-use-new-organization-metadata-structure";
186185
public const string PM24032_NewNavigationPremiumUpgradeButton = "pm-24032-new-navigation-premium-upgrade-button";
187186
public const string PM23713_PremiumBadgeOpensNewPremiumUpgradeDialog = "pm-23713-premium-badge-opens-new-premium-upgrade-dialog";
188187
public const string PM26793_FetchPremiumPriceFromPricingService = "pm-26793-fetch-premium-price-from-pricing-service";

test/Api.Test/Billing/Controllers/OrganizationBillingControllerTests.cs

Lines changed: 0 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
using Bit.Api.Billing.Controllers;
22
using Bit.Core.AdminConsole.Entities;
33
using Bit.Core.Billing.Models;
4-
using Bit.Core.Billing.Organizations.Models;
5-
using Bit.Core.Billing.Organizations.Services;
64
using Bit.Core.Billing.Services;
75
using Bit.Core.Context;
86
using Bit.Core.Repositories;
@@ -20,50 +18,6 @@ namespace Bit.Api.Test.Billing.Controllers;
2018
[SutProviderCustomize]
2119
public class OrganizationBillingControllerTests
2220
{
23-
[Theory, BitAutoData]
24-
public async Task GetMetadataAsync_Unauthorized_ReturnsUnauthorized(
25-
Guid organizationId,
26-
SutProvider<OrganizationBillingController> sutProvider)
27-
{
28-
sutProvider.GetDependency<ICurrentContext>().AccessMembersTab(organizationId).Returns(false);
29-
30-
var result = await sutProvider.Sut.GetMetadataAsync(organizationId);
31-
32-
AssertUnauthorized(result);
33-
}
34-
35-
[Theory, BitAutoData]
36-
public async Task GetMetadataAsync_MetadataNull_NotFound(
37-
Guid organizationId,
38-
SutProvider<OrganizationBillingController> sutProvider)
39-
{
40-
sutProvider.GetDependency<ICurrentContext>().OrganizationUser(organizationId).Returns(true);
41-
sutProvider.GetDependency<IOrganizationBillingService>().GetMetadata(organizationId).Returns((OrganizationMetadata)null);
42-
43-
var result = await sutProvider.Sut.GetMetadataAsync(organizationId);
44-
45-
AssertNotFound(result);
46-
}
47-
48-
[Theory, BitAutoData]
49-
public async Task GetMetadataAsync_OK(
50-
Guid organizationId,
51-
SutProvider<OrganizationBillingController> sutProvider)
52-
{
53-
sutProvider.GetDependency<ICurrentContext>().OrganizationUser(organizationId).Returns(true);
54-
sutProvider.GetDependency<IOrganizationBillingService>().GetMetadata(organizationId)
55-
.Returns(new OrganizationMetadata(true, 10));
56-
57-
var result = await sutProvider.Sut.GetMetadataAsync(organizationId);
58-
59-
Assert.IsType<Ok<OrganizationMetadata>>(result);
60-
61-
var response = ((Ok<OrganizationMetadata>)result).Value;
62-
63-
Assert.True(response.IsOnSecretsManagerStandalone);
64-
Assert.Equal(10, response.OrganizationOccupiedSeats);
65-
}
66-
6721
[Theory, BitAutoData]
6822
public async Task GetHistoryAsync_Unauthorized_ReturnsUnauthorized(
6923
Guid organizationId,

test/Core.Test/Billing/Services/OrganizationBillingServiceTests.cs

Lines changed: 0 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
using Bit.Core.Billing.Payment.Queries;
88
using Bit.Core.Billing.Pricing;
99
using Bit.Core.Billing.Services;
10-
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
1110
using Bit.Core.Repositories;
1211
using Bit.Core.Test.Billing.Mocks;
1312
using Bit.Test.Common.AutoFixture;
@@ -21,110 +20,6 @@ namespace Bit.Core.Test.Billing.Services;
2120
[SutProviderCustomize]
2221
public class OrganizationBillingServiceTests
2322
{
24-
#region GetMetadata
25-
26-
[Theory, BitAutoData]
27-
public async Task GetMetadata_Succeeds(
28-
Guid organizationId,
29-
Organization organization,
30-
SutProvider<OrganizationBillingService> sutProvider)
31-
{
32-
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organizationId).Returns(organization);
33-
sutProvider.GetDependency<IPricingClient>().ListPlans().Returns(MockPlans.Plans.ToList());
34-
35-
sutProvider.GetDependency<IPricingClient>().GetPlanOrThrow(organization.PlanType)
36-
.Returns(MockPlans.Get(organization.PlanType));
37-
38-
var subscriberService = sutProvider.GetDependency<ISubscriberService>();
39-
var organizationSeatCount = new OrganizationSeatCounts { Users = 1, Sponsored = 0 };
40-
var customer = new Customer();
41-
42-
subscriberService
43-
.GetCustomer(organization)
44-
.Returns(customer);
45-
46-
subscriberService.GetSubscription(organization, Arg.Is<SubscriptionGetOptions>(options =>
47-
options.Expand.Contains("discounts.coupon.applies_to"))).Returns(new Subscription
48-
{
49-
Discounts =
50-
[
51-
new Discount
52-
{
53-
Coupon = new Coupon
54-
{
55-
Id = StripeConstants.CouponIDs.SecretsManagerStandalone,
56-
AppliesTo = new CouponAppliesTo
57-
{
58-
Products = ["product_id"]
59-
}
60-
}
61-
}
62-
],
63-
Items = new StripeList<SubscriptionItem>
64-
{
65-
Data =
66-
[
67-
new SubscriptionItem
68-
{
69-
Plan = new Plan
70-
{
71-
ProductId = "product_id"
72-
}
73-
}
74-
]
75-
}
76-
});
77-
78-
sutProvider.GetDependency<IOrganizationRepository>()
79-
.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id)
80-
.Returns(new OrganizationSeatCounts { Users = 1, Sponsored = 0 });
81-
82-
var metadata = await sutProvider.Sut.GetMetadata(organizationId);
83-
84-
Assert.True(metadata!.IsOnSecretsManagerStandalone);
85-
}
86-
87-
#endregion
88-
89-
#region GetMetadata - Null Customer or Subscription
90-
91-
[Theory, BitAutoData]
92-
public async Task GetMetadata_WhenCustomerOrSubscriptionIsNull_ReturnsDefaultMetadata(
93-
Guid organizationId,
94-
Organization organization,
95-
SutProvider<OrganizationBillingService> sutProvider)
96-
{
97-
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organizationId).Returns(organization);
98-
99-
sutProvider.GetDependency<IPricingClient>().ListPlans().Returns(MockPlans.Plans.ToList());
100-
101-
sutProvider.GetDependency<IPricingClient>().GetPlanOrThrow(organization.PlanType)
102-
.Returns(MockPlans.Get(organization.PlanType));
103-
104-
sutProvider.GetDependency<IOrganizationRepository>()
105-
.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id)
106-
.Returns(new OrganizationSeatCounts { Users = 1, Sponsored = 0 });
107-
108-
var subscriberService = sutProvider.GetDependency<ISubscriberService>();
109-
110-
// Set up subscriber service to return null for customer
111-
subscriberService
112-
.GetCustomer(organization)
113-
.Returns((Customer)null);
114-
115-
// Set up subscriber service to return null for subscription
116-
subscriberService.GetSubscription(organization, Arg.Is<SubscriptionGetOptions>(options =>
117-
options.Expand.Contains("discounts.coupon.applies_to"))).Returns((Subscription)null);
118-
119-
var metadata = await sutProvider.Sut.GetMetadata(organizationId);
120-
121-
Assert.NotNull(metadata);
122-
Assert.False(metadata!.IsOnSecretsManagerStandalone);
123-
Assert.Equal(1, metadata.OrganizationOccupiedSeats);
124-
}
125-
126-
#endregion
127-
12823
#region Finalize - Trial Settings
12924

13025
[Theory, BitAutoData]

0 commit comments

Comments
 (0)