mirror of
https://github.com/bitwarden/server
synced 2026-02-14 23:45:11 +00:00
[PM 30100][Server] Subscription Discount Database Infrastructure (#6936)
* Implement the detail Subscription Discount Database Infrastructure * Change string to string list * fix lint error * Create all missing database object definition files * Regenerate EF migrations with Designer files The previous migrations were missing .Designer.cs files. This commit: - Removes the incomplete migration files - Regenerates all three provider migrations (MySQL, Postgres, SQLite) with proper Designer files - Updates DatabaseContextModelSnapshot.cs for each provider 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * fix failing database * Resolve the lint warnings * Resolve the database failure * Fix the build Lint * resolve the dbops reviews * Add the default value --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
using System.Text.Json;
|
||||
using Bit.Infrastructure.EntityFramework.Billing.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
|
||||
namespace Bit.Infrastructure.EntityFramework.Billing.Configurations;
|
||||
|
||||
public class SubscriptionDiscountEntityTypeConfiguration : IEntityTypeConfiguration<SubscriptionDiscount>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<SubscriptionDiscount> builder)
|
||||
{
|
||||
builder
|
||||
.Property(t => t.Id)
|
||||
.ValueGeneratedNever();
|
||||
|
||||
builder
|
||||
.HasIndex(sd => sd.StripeCouponId)
|
||||
.IsUnique();
|
||||
|
||||
builder
|
||||
.Property(sd => sd.StripeProductIds)
|
||||
.HasConversion(
|
||||
v => v == null ? null : JsonSerializer.Serialize(v, (JsonSerializerOptions?)null),
|
||||
v => v == null ? null : JsonSerializer.Deserialize<List<string>>(v, (JsonSerializerOptions?)null),
|
||||
new ValueComparer<ICollection<string>?>(
|
||||
(c1, c2) => (c1 == null && c2 == null) || (c1 != null && c2 != null && c1.SequenceEqual(c2)),
|
||||
c => c == null ? 0 : c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),
|
||||
c => c == null ? null : c.ToList()));
|
||||
|
||||
builder
|
||||
.Property(sd => sd.PercentOff)
|
||||
.HasPrecision(5, 2);
|
||||
|
||||
builder
|
||||
.HasIndex(sd => new { sd.StartDate, sd.EndDate })
|
||||
.IsClustered(false)
|
||||
.HasDatabaseName("IX_SubscriptionDiscount_DateRange");
|
||||
|
||||
builder.ToTable(nameof(SubscriptionDiscount));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#nullable enable
|
||||
|
||||
using AutoMapper;
|
||||
|
||||
namespace Bit.Infrastructure.EntityFramework.Billing.Models;
|
||||
|
||||
// ReSharper disable once ClassWithVirtualMembersNeverInherited.Global
|
||||
public class SubscriptionDiscount : Core.Billing.Subscriptions.Entities.SubscriptionDiscount
|
||||
{
|
||||
}
|
||||
|
||||
public class SubscriptionDiscountMapperProfile : Profile
|
||||
{
|
||||
public SubscriptionDiscountMapperProfile()
|
||||
{
|
||||
CreateMap<Core.Billing.Subscriptions.Entities.SubscriptionDiscount, SubscriptionDiscount>().ReverseMap();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using AutoMapper;
|
||||
using Bit.Core.Billing.Subscriptions.Entities;
|
||||
using Bit.Core.Billing.Subscriptions.Repositories;
|
||||
using Bit.Infrastructure.EntityFramework.Repositories;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using EFSubscriptionDiscount = Bit.Infrastructure.EntityFramework.Billing.Models.SubscriptionDiscount;
|
||||
|
||||
namespace Bit.Infrastructure.EntityFramework.Billing.Repositories;
|
||||
|
||||
public class SubscriptionDiscountRepository(
|
||||
IMapper mapper,
|
||||
IServiceScopeFactory serviceScopeFactory)
|
||||
: Repository<SubscriptionDiscount, EFSubscriptionDiscount, Guid>(
|
||||
serviceScopeFactory,
|
||||
mapper,
|
||||
context => context.SubscriptionDiscounts), ISubscriptionDiscountRepository
|
||||
{
|
||||
public async Task<ICollection<SubscriptionDiscount>> GetActiveDiscountsAsync()
|
||||
{
|
||||
using var serviceScope = ServiceScopeFactory.CreateScope();
|
||||
|
||||
var databaseContext = GetDatabaseContext(serviceScope);
|
||||
|
||||
var query =
|
||||
from subscriptionDiscount in databaseContext.SubscriptionDiscounts
|
||||
where subscriptionDiscount.StartDate <= DateTime.UtcNow
|
||||
&& subscriptionDiscount.EndDate >= DateTime.UtcNow
|
||||
select subscriptionDiscount;
|
||||
|
||||
var results = await query.ToArrayAsync();
|
||||
|
||||
return Mapper.Map<List<SubscriptionDiscount>>(results);
|
||||
}
|
||||
|
||||
public async Task<SubscriptionDiscount?> GetByStripeCouponIdAsync(string stripeCouponId)
|
||||
{
|
||||
using var serviceScope = ServiceScopeFactory.CreateScope();
|
||||
|
||||
var databaseContext = GetDatabaseContext(serviceScope);
|
||||
|
||||
var query =
|
||||
from subscriptionDiscount in databaseContext.SubscriptionDiscounts
|
||||
where subscriptionDiscount.StripeCouponId == stripeCouponId
|
||||
select subscriptionDiscount;
|
||||
|
||||
var result = await query.FirstOrDefaultAsync();
|
||||
|
||||
return result == null ? null : Mapper.Map<SubscriptionDiscount>(result);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
using Bit.Core.Auth.Repositories;
|
||||
using Bit.Core.Billing.Organizations.Repositories;
|
||||
using Bit.Core.Billing.Providers.Repositories;
|
||||
using Bit.Core.Billing.Subscriptions.Repositories;
|
||||
using Bit.Core.Dirt.Reports.Repositories;
|
||||
using Bit.Core.Dirt.Repositories;
|
||||
using Bit.Core.Enums;
|
||||
@@ -102,6 +103,7 @@ public static class EntityFrameworkServiceCollectionExtensions
|
||||
services.AddSingleton<IWebAuthnCredentialRepository, WebAuthnCredentialRepository>();
|
||||
services.AddSingleton<IProviderPlanRepository, ProviderPlanRepository>();
|
||||
services.AddSingleton<IProviderInvoiceItemRepository, ProviderInvoiceItemRepository>();
|
||||
services.AddSingleton<ISubscriptionDiscountRepository, SubscriptionDiscountRepository>();
|
||||
services.AddSingleton<INotificationRepository, NotificationRepository>();
|
||||
services.AddSingleton<INotificationStatusRepository, NotificationStatusRepository>();
|
||||
services
|
||||
|
||||
@@ -79,6 +79,7 @@ public class DatabaseContext : DbContext
|
||||
public DbSet<WebAuthnCredential> WebAuthnCredentials { get; set; }
|
||||
public DbSet<ProviderPlan> ProviderPlans { get; set; }
|
||||
public DbSet<ProviderInvoiceItem> ProviderInvoiceItems { get; set; }
|
||||
public DbSet<SubscriptionDiscount> SubscriptionDiscounts { get; set; }
|
||||
public DbSet<Notification> Notifications { get; set; }
|
||||
public DbSet<NotificationStatus> NotificationStatuses { get; set; }
|
||||
public DbSet<ClientOrganizationMigrationRecord> ClientOrganizationMigrationRecords { get; set; }
|
||||
|
||||
Reference in New Issue
Block a user