1
0
mirror of https://github.com/bitwarden/server.git synced 2024-12-12 15:26:48 +01:00

[PM-11127] Write OrganizationInstallation record when license is retrieved (#5090)

* Add SQL files

* Add SQL Server migration

* Add Core entity

* Add Dapper repository

* Add EF repository

* Add EF migrations

* Save OrganizationInstallation during GetLicense invocation

* Run dotnet format
This commit is contained in:
Alex Morask 2024-12-11 13:55:00 -05:00 committed by GitHub
parent 4c502f8cc8
commit 2d891b396a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 9751 additions and 2 deletions

View File

@ -6,7 +6,9 @@ using Bit.Api.Models.Request.Organizations;
using Bit.Api.Models.Response;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.Billing.Constants;
using Bit.Core.Billing.Entities;
using Bit.Core.Billing.Models;
using Bit.Core.Billing.Repositories;
using Bit.Core.Billing.Services;
using Bit.Core.Context;
using Bit.Core.Enums;
@ -42,7 +44,8 @@ public class OrganizationsController(
IUpgradeOrganizationPlanCommand upgradeOrganizationPlanCommand,
IAddSecretsManagerSubscriptionCommand addSecretsManagerSubscriptionCommand,
IReferenceEventService referenceEventService,
ISubscriberService subscriberService)
ISubscriberService subscriberService,
IOrganizationInstallationRepository organizationInstallationRepository)
: Controller
{
[HttpGet("{id:guid}/subscription")]
@ -97,6 +100,8 @@ public class OrganizationsController(
throw new NotFoundException();
}
await SaveOrganizationInstallationAsync(id, installationId);
return license;
}
@ -366,4 +371,24 @@ public class OrganizationsController(
return await organizationRepository.GetByIdAsync(id);
}
private async Task SaveOrganizationInstallationAsync(Guid organizationId, Guid installationId)
{
var organizationInstallation =
await organizationInstallationRepository.GetByInstallationIdAsync(installationId);
if (organizationInstallation == null)
{
await organizationInstallationRepository.CreateAsync(new OrganizationInstallation
{
OrganizationId = organizationId,
InstallationId = installationId
});
}
else if (organizationInstallation.OrganizationId == organizationId)
{
organizationInstallation.RevisionDate = DateTime.UtcNow;
await organizationInstallationRepository.ReplaceAsync(organizationInstallation);
}
}
}

View File

@ -0,0 +1,24 @@
using Bit.Core.Entities;
using Bit.Core.Utilities;
namespace Bit.Core.Billing.Entities;
#nullable enable
public class OrganizationInstallation : ITableObject<Guid>
{
public Guid Id { get; set; }
public Guid OrganizationId { get; set; }
public Guid InstallationId { get; set; }
public DateTime CreationDate { get; internal set; } = DateTime.UtcNow;
public DateTime? RevisionDate { get; set; }
public void SetNewId()
{
if (Id == default)
{
Id = CoreHelpers.GenerateComb();
}
}
}

View File

@ -0,0 +1,10 @@
using Bit.Core.Billing.Entities;
using Bit.Core.Repositories;
namespace Bit.Core.Billing.Repositories;
public interface IOrganizationInstallationRepository : IRepository<OrganizationInstallation, Guid>
{
Task<OrganizationInstallation> GetByInstallationIdAsync(Guid installationId);
Task<ICollection<OrganizationInstallation>> GetByOrganizationIdAsync(Guid organizationId);
}

View File

@ -0,0 +1,39 @@
using System.Data;
using Bit.Core.Billing.Entities;
using Bit.Core.Billing.Repositories;
using Bit.Core.Settings;
using Bit.Infrastructure.Dapper.Repositories;
using Dapper;
using Microsoft.Data.SqlClient;
namespace Bit.Infrastructure.Dapper.Billing.Repositories;
public class OrganizationInstallationRepository(
GlobalSettings globalSettings) : Repository<OrganizationInstallation, Guid>(
globalSettings.SqlServer.ConnectionString,
globalSettings.SqlServer.ReadOnlyConnectionString), IOrganizationInstallationRepository
{
public async Task<OrganizationInstallation> GetByInstallationIdAsync(Guid installationId)
{
var sqlConnection = new SqlConnection(ConnectionString);
var results = await sqlConnection.QueryAsync<OrganizationInstallation>(
"[dbo].[OrganizationInstallation_ReadByInstallationId]",
new { InstallationId = installationId },
commandType: CommandType.StoredProcedure);
return results.FirstOrDefault();
}
public async Task<ICollection<OrganizationInstallation>> GetByOrganizationIdAsync(Guid organizationId)
{
var sqlConnection = new SqlConnection(ConnectionString);
var results = await sqlConnection.QueryAsync<OrganizationInstallation>(
"[dbo].[OrganizationInstallation_ReadByOrganizationId]",
new { OrganizationId = organizationId },
commandType: CommandType.StoredProcedure);
return results.ToArray();
}
}

View File

@ -63,6 +63,7 @@ public static class DapperServiceCollectionExtensions
services.AddSingleton<IPasswordHealthReportApplicationRepository, PasswordHealthReportApplicationRepository>();
services.AddSingleton<ISecurityTaskRepository, SecurityTaskRepository>();
services.AddSingleton<IUserAsymmetricKeysRepository, UserAsymmetricKeysRepository>();
services.AddSingleton<IOrganizationInstallationRepository, OrganizationInstallationRepository>();
if (selfHosted)
{

View File

@ -0,0 +1,29 @@
using Bit.Infrastructure.EntityFramework.Billing.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Bit.Infrastructure.EntityFramework.Billing.Configurations;
public class OrganizationInstallationEntityTypeConfiguration : IEntityTypeConfiguration<OrganizationInstallation>
{
public void Configure(EntityTypeBuilder<OrganizationInstallation> builder)
{
builder
.Property(oi => oi.Id)
.ValueGeneratedNever();
builder
.HasKey(oi => oi.Id)
.IsClustered();
builder
.HasIndex(oi => oi.OrganizationId)
.IsClustered(false);
builder
.HasIndex(oi => oi.InstallationId)
.IsClustered(false);
builder.ToTable(nameof(OrganizationInstallation));
}
}

View File

@ -0,0 +1,19 @@
using AutoMapper;
using Bit.Infrastructure.EntityFramework.AdminConsole.Models;
using Bit.Infrastructure.EntityFramework.Models;
namespace Bit.Infrastructure.EntityFramework.Billing.Models;
public class OrganizationInstallation : Core.Billing.Entities.OrganizationInstallation
{
public virtual Installation Installation { get; set; }
public virtual Organization Organization { get; set; }
}
public class OrganizationInstallationMapperProfile : Profile
{
public OrganizationInstallationMapperProfile()
{
CreateMap<Core.Billing.Entities.OrganizationInstallation, OrganizationInstallation>().ReverseMap();
}
}

View File

@ -0,0 +1,45 @@
using AutoMapper;
using Bit.Core.Billing.Entities;
using Bit.Core.Billing.Repositories;
using Bit.Infrastructure.EntityFramework.Repositories;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using EFOrganizationInstallation = Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationInstallation;
namespace Bit.Infrastructure.EntityFramework.Billing.Repositories;
public class OrganizationInstallationRepository(
IMapper mapper,
IServiceScopeFactory serviceScopeFactory) : Repository<OrganizationInstallation, EFOrganizationInstallation, Guid>(
serviceScopeFactory,
mapper,
context => context.OrganizationInstallations), IOrganizationInstallationRepository
{
public async Task<OrganizationInstallation> GetByInstallationIdAsync(Guid installationId)
{
using var serviceScope = ServiceScopeFactory.CreateScope();
var databaseContext = GetDatabaseContext(serviceScope);
var query =
from organizationInstallation in databaseContext.OrganizationInstallations
where organizationInstallation.Id == installationId
select organizationInstallation;
return await query.FirstOrDefaultAsync();
}
public async Task<ICollection<OrganizationInstallation>> GetByOrganizationIdAsync(Guid organizationId)
{
using var serviceScope = ServiceScopeFactory.CreateScope();
var databaseContext = GetDatabaseContext(serviceScope);
var query =
from organizationInstallation in databaseContext.OrganizationInstallations
where organizationInstallation.OrganizationId == organizationId
select organizationInstallation;
return await query.ToArrayAsync();
}
}

View File

@ -78,6 +78,7 @@ public class DatabaseContext : DbContext
public DbSet<ClientOrganizationMigrationRecord> ClientOrganizationMigrationRecords { get; set; }
public DbSet<PasswordHealthReportApplication> PasswordHealthReportApplications { get; set; }
public DbSet<SecurityTask> SecurityTasks { get; set; }
public DbSet<OrganizationInstallation> OrganizationInstallations { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{

View File

@ -0,0 +1,27 @@
CREATE PROCEDURE [dbo].[OrganizationInstallation_Create]
@Id UNIQUEIDENTIFIER OUTPUT,
@OrganizationId UNIQUEIDENTIFIER,
@InstallationId UNIQUEIDENTIFIER,
@CreationDate DATETIME2 (7),
@RevisionDate DATETIME2 (7) = NULL
AS
BEGIN
SET NOCOUNT ON
INSERT INTO [dbo].[OrganizationInstallation]
(
[Id],
[OrganizationId],
[InstallationId],
[CreationDate],
[RevisionDate]
)
VALUES
(
@Id,
@OrganizationId,
@InstallationId,
@CreationDate,
@RevisionDate
)
END

View File

@ -0,0 +1,12 @@
CREATE PROCEDURE [dbo].[OrganizationInstallation_DeleteById]
@Id UNIQUEIDENTIFIER
AS
BEGIN
SET NOCOUNT ON
DELETE
FROM
[dbo].[OrganizationInstallation]
WHERE
[Id] = @Id
END

View File

@ -0,0 +1,13 @@
CREATE PROCEDURE [dbo].[OrganizationInstallation_ReadById]
@Id UNIQUEIDENTIFIER
AS
BEGIN
SET NOCOUNT ON
SELECT
*
FROM
[dbo].[OrganizationInstallationView]
WHERE
[Id] = @Id
END

View File

@ -0,0 +1,13 @@
CREATE PROCEDURE [dbo].[OrganizationInstallation_ReadByInstallationId]
@InstallationId UNIQUEIDENTIFIER
AS
BEGIN
SET NOCOUNT ON
SELECT
*
FROM
[dbo].[OrganizationInstallationView]
WHERE
[InstallationId] = @InstallationId
END

View File

@ -0,0 +1,13 @@
CREATE PROCEDURE [dbo].[OrganizationInstallation_ReadByOrganizationId]
@OrganizationId UNIQUEIDENTIFIER
AS
BEGIN
SET NOCOUNT ON
SELECT
*
FROM
[dbo].[OrganizationInstallationView]
WHERE
[OrganizationId] = @OrganizationId
END

View File

@ -0,0 +1,17 @@
CREATE PROCEDURE [dbo].[OrganizationInstallation_Update]
@Id UNIQUEIDENTIFIER OUTPUT,
@OrganizationId UNIQUEIDENTIFIER,
@InstallationId UNIQUEIDENTIFIER,
@CreationDate DATETIME2 (7),
@RevisionDate DATETIME2 (7)
AS
BEGIN
SET NOCOUNT ON
UPDATE
[dbo].[OrganizationInstallation]
SET
[RevisionDate] = @RevisionDate
WHERE
[Id] = @Id
END

View File

@ -0,0 +1,18 @@
CREATE TABLE [dbo].[OrganizationInstallation] (
[Id] UNIQUEIDENTIFIER NOT NULL,
[OrganizationId] UNIQUEIDENTIFIER NOT NULL,
[InstallationId] UNIQUEIDENTIFIER NOT NULL,
[CreationDate] DATETIME2 (7) NOT NULL,
[RevisionDate] DATETIME2 (7) NULL,
CONSTRAINT [PK_OrganizationInstallation] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_OrganizationInstallation_Organization] FOREIGN KEY ([OrganizationId]) REFERENCES [dbo].[Organization] ([Id]) ON DELETE CASCADE,
CONSTRAINT [FK_OrganizationInstallation_Installation] FOREIGN KEY ([InstallationId]) REFERENCES [dbo].[Installation] ([Id]) ON DELETE CASCADE
);
GO
CREATE NONCLUSTERED INDEX [IX_OrganizationInstallation_OrganizationId]
ON [dbo].[OrganizationInstallation]([OrganizationId] ASC);
GO
CREATE NONCLUSTERED INDEX [IX_OrganizationInstallation_InstallationId]
ON [dbo].[OrganizationInstallation]([InstallationId] ASC);

View File

@ -0,0 +1,6 @@
CREATE VIEW [dbo].[OrganizationInstallationView]
AS
SELECT
*
FROM
[dbo].[OrganizationInstallation];

View File

@ -10,6 +10,7 @@ using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Models.Data;
using Bit.Core.Auth.Repositories;
using Bit.Core.Auth.Services;
using Bit.Core.Billing.Repositories;
using Bit.Core.Billing.Services;
using Bit.Core.Context;
using Bit.Core.Entities;
@ -47,6 +48,7 @@ public class OrganizationsControllerTests : IDisposable
private readonly IReferenceEventService _referenceEventService;
private readonly ISubscriberService _subscriberService;
private readonly IRemoveOrganizationUserCommand _removeOrganizationUserCommand;
private readonly IOrganizationInstallationRepository _organizationInstallationRepository;
private readonly OrganizationsController _sut;
@ -70,6 +72,7 @@ public class OrganizationsControllerTests : IDisposable
_referenceEventService = Substitute.For<IReferenceEventService>();
_subscriberService = Substitute.For<ISubscriberService>();
_removeOrganizationUserCommand = Substitute.For<IRemoveOrganizationUserCommand>();
_organizationInstallationRepository = Substitute.For<IOrganizationInstallationRepository>();
_sut = new OrganizationsController(
_organizationRepository,
@ -85,7 +88,8 @@ public class OrganizationsControllerTests : IDisposable
_upgradeOrganizationPlanCommand,
_addSecretsManagerSubscriptionCommand,
_referenceEventService,
_subscriberService);
_subscriberService,
_organizationInstallationRepository);
}
public void Dispose()

View File

@ -0,0 +1,158 @@
-- OrganizationInstallation
-- Table
IF OBJECT_ID('[dbo].[OrganizationInstallation]') IS NULL
BEGIN
CREATE TABLE [dbo].[OrganizationInstallation] (
[Id] UNIQUEIDENTIFIER NOT NULL,
[OrganizationId] UNIQUEIDENTIFIER NOT NULL,
[InstallationId] UNIQUEIDENTIFIER NOT NULL,
[CreationDate] DATETIME2 (7) NOT NULL,
[RevisionDate] DATETIME2 (7) NULL,
CONSTRAINT [PK_OrganizationInstallation] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_OrganizationInstallation_Organization] FOREIGN KEY ([OrganizationId]) REFERENCES [dbo].[Organization] ([Id]) ON DELETE CASCADE,
CONSTRAINT [FK_OrganizationInstallation_Installation] FOREIGN KEY ([InstallationId]) REFERENCES [dbo].[Installation] ([Id]) ON DELETE CASCADE
);
END
GO
-- Indexes
IF NOT EXISTS(SELECT name FROM sys.indexes WHERE name = 'IX_OrganizationInstallation_OrganizationId')
BEGIN
CREATE NONCLUSTERED INDEX [IX_OrganizationInstallation_OrganizationId]
ON [dbo].[OrganizationInstallation]([OrganizationId] ASC);
END
IF NOT EXISTS(SELECT name FROM sys.indexes WHERE name = 'IX_OrganizationInstallation_InstallationId')
BEGIN
CREATE NONCLUSTERED INDEX [IX_OrganizationInstallation_InstallationId]
ON [dbo].[OrganizationInstallation]([InstallationId] ASC);
END
-- View
IF EXISTS(SELECT * FROM sys.views WHERE [Name] = 'OrganizationInstallationView')
BEGIN
DROP VIEW [dbo].[OrganizationInstallationView];
END
GO
CREATE VIEW [dbo].[OrganizationInstallationView]
AS
SELECT
*
FROM
[dbo].[OrganizationInstallation]
GO
-- Stored Procedures: Create
CREATE OR ALTER PROCEDURE [dbo].[OrganizationInstallation_Create]
@Id UNIQUEIDENTIFIER OUTPUT,
@OrganizationId UNIQUEIDENTIFIER,
@InstallationId UNIQUEIDENTIFIER,
@CreationDate DATETIME2 (7),
@RevisionDate DATETIME2 (7) = NULL
AS
BEGIN
SET NOCOUNT ON
INSERT INTO [dbo].[OrganizationInstallation]
(
[Id],
[OrganizationId],
[InstallationId],
[CreationDate],
[RevisionDate]
)
VALUES
(
@Id,
@OrganizationId,
@InstallationId,
@CreationDate,
@RevisionDate
)
END
GO
-- Stored Procedures: DeleteById
CREATE OR ALTER PROCEDURE [dbo].[OrganizationInstallation_DeleteById]
@Id UNIQUEIDENTIFIER
AS
BEGIN
SET NOCOUNT ON
DELETE
FROM
[dbo].[OrganizationInstallation]
WHERE
[Id] = @Id
END
GO
-- Stored Procedures: ReadById
CREATE PROCEDURE [dbo].[OrganizationInstallation_ReadById]
@Id UNIQUEIDENTIFIER
AS
BEGIN
SET NOCOUNT ON
SELECT
*
FROM
[dbo].[OrganizationInstallationView]
WHERE
[Id] = @Id
END
GO
-- Stored Procedures: ReadByInstallationId
CREATE PROCEDURE [dbo].[OrganizationInstallation_ReadByInstallationId]
@InstallationId UNIQUEIDENTIFIER
AS
BEGIN
SET NOCOUNT ON
SELECT
*
FROM
[dbo].[OrganizationInstallationView]
WHERE
[InstallationId] = @InstallationId
END
GO
-- Stored Procedures: ReadByOrganizationId
CREATE PROCEDURE [dbo].[OrganizationInstallation_ReadByOrganizationId]
@OrganizationId UNIQUEIDENTIFIER
AS
BEGIN
SET NOCOUNT ON
SELECT
*
FROM
[dbo].[OrganizationInstallationView]
WHERE
[OrganizationId] = @OrganizationId
END
GO
-- Stored Procedures: Update
CREATE PROCEDURE [dbo].[OrganizationInstallation_Update]
@Id UNIQUEIDENTIFIER OUTPUT,
@OrganizationId UNIQUEIDENTIFIER,
@InstallationId UNIQUEIDENTIFIER,
@CreationDate DATETIME2 (7),
@RevisionDate DATETIME2 (7)
AS
BEGIN
SET NOCOUNT ON
UPDATE
[dbo].[OrganizationInstallation]
SET
[RevisionDate] = @RevisionDate
WHERE
[Id] = @Id
END
GO

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,58 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Bit.MySqlMigrations.Migrations;
/// <inheritdoc />
public partial class AddTable_OrganizationInstallation : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "OrganizationInstallation",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
OrganizationId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
InstallationId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
CreationDate = table.Column<DateTime>(type: "datetime(6)", nullable: false),
RevisionDate = table.Column<DateTime>(type: "datetime(6)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_OrganizationInstallation", x => x.Id);
table.ForeignKey(
name: "FK_OrganizationInstallation_Installation_InstallationId",
column: x => x.InstallationId,
principalTable: "Installation",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_OrganizationInstallation_Organization_OrganizationId",
column: x => x.OrganizationId,
principalTable: "Organization",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateIndex(
name: "IX_OrganizationInstallation_InstallationId",
table: "OrganizationInstallation",
column: "InstallationId");
migrationBuilder.CreateIndex(
name: "IX_OrganizationInstallation_OrganizationId",
table: "OrganizationInstallation",
column: "OrganizationId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "OrganizationInstallation");
}
}

View File

@ -737,6 +737,35 @@ namespace Bit.MySqlMigrations.Migrations
b.ToTable("ClientOrganizationMigrationRecord", (string)null);
});
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationInstallation", b =>
{
b.Property<Guid>("Id")
.HasColumnType("char(36)");
b.Property<DateTime>("CreationDate")
.HasColumnType("datetime(6)");
b.Property<Guid>("InstallationId")
.HasColumnType("char(36)");
b.Property<Guid>("OrganizationId")
.HasColumnType("char(36)");
b.Property<DateTime?>("RevisionDate")
.HasColumnType("datetime(6)");
b.HasKey("Id")
.HasAnnotation("SqlServer:Clustered", true);
b.HasIndex("InstallationId")
.HasAnnotation("SqlServer:Clustered", false);
b.HasIndex("OrganizationId")
.HasAnnotation("SqlServer:Clustered", false);
b.ToTable("OrganizationInstallation", (string)null);
});
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderInvoiceItem", b =>
{
b.Property<Guid>("Id")
@ -2336,6 +2365,25 @@ namespace Bit.MySqlMigrations.Migrations
b.Navigation("User");
});
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationInstallation", b =>
{
b.HasOne("Bit.Infrastructure.EntityFramework.Models.Installation", "Installation")
.WithMany()
.HasForeignKey("InstallationId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")
.WithMany()
.HasForeignKey("OrganizationId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Installation");
b.Navigation("Organization");
});
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderInvoiceItem", b =>
{
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider")

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,57 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Bit.PostgresMigrations.Migrations;
/// <inheritdoc />
public partial class AddTable_OrganizationInstallation : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "OrganizationInstallation",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
OrganizationId = table.Column<Guid>(type: "uuid", nullable: false),
InstallationId = table.Column<Guid>(type: "uuid", nullable: false),
CreationDate = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
RevisionDate = table.Column<DateTime>(type: "timestamp with time zone", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_OrganizationInstallation", x => x.Id);
table.ForeignKey(
name: "FK_OrganizationInstallation_Installation_InstallationId",
column: x => x.InstallationId,
principalTable: "Installation",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_OrganizationInstallation_Organization_OrganizationId",
column: x => x.OrganizationId,
principalTable: "Organization",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_OrganizationInstallation_InstallationId",
table: "OrganizationInstallation",
column: "InstallationId");
migrationBuilder.CreateIndex(
name: "IX_OrganizationInstallation_OrganizationId",
table: "OrganizationInstallation",
column: "OrganizationId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "OrganizationInstallation");
}
}

View File

@ -742,6 +742,35 @@ namespace Bit.PostgresMigrations.Migrations
b.ToTable("ClientOrganizationMigrationRecord", (string)null);
});
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationInstallation", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<DateTime>("CreationDate")
.HasColumnType("timestamp with time zone");
b.Property<Guid>("InstallationId")
.HasColumnType("uuid");
b.Property<Guid>("OrganizationId")
.HasColumnType("uuid");
b.Property<DateTime?>("RevisionDate")
.HasColumnType("timestamp with time zone");
b.HasKey("Id")
.HasAnnotation("SqlServer:Clustered", true);
b.HasIndex("InstallationId")
.HasAnnotation("SqlServer:Clustered", false);
b.HasIndex("OrganizationId")
.HasAnnotation("SqlServer:Clustered", false);
b.ToTable("OrganizationInstallation", (string)null);
});
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderInvoiceItem", b =>
{
b.Property<Guid>("Id")
@ -2342,6 +2371,25 @@ namespace Bit.PostgresMigrations.Migrations
b.Navigation("User");
});
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationInstallation", b =>
{
b.HasOne("Bit.Infrastructure.EntityFramework.Models.Installation", "Installation")
.WithMany()
.HasForeignKey("InstallationId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")
.WithMany()
.HasForeignKey("OrganizationId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Installation");
b.Navigation("Organization");
});
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderInvoiceItem", b =>
{
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider")

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,57 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Bit.SqliteMigrations.Migrations;
/// <inheritdoc />
public partial class AddTable_OrganizationInstallation : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "OrganizationInstallation",
columns: table => new
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
OrganizationId = table.Column<Guid>(type: "TEXT", nullable: false),
InstallationId = table.Column<Guid>(type: "TEXT", nullable: false),
CreationDate = table.Column<DateTime>(type: "TEXT", nullable: false),
RevisionDate = table.Column<DateTime>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_OrganizationInstallation", x => x.Id);
table.ForeignKey(
name: "FK_OrganizationInstallation_Installation_InstallationId",
column: x => x.InstallationId,
principalTable: "Installation",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_OrganizationInstallation_Organization_OrganizationId",
column: x => x.OrganizationId,
principalTable: "Organization",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_OrganizationInstallation_InstallationId",
table: "OrganizationInstallation",
column: "InstallationId");
migrationBuilder.CreateIndex(
name: "IX_OrganizationInstallation_OrganizationId",
table: "OrganizationInstallation",
column: "OrganizationId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "OrganizationInstallation");
}
}

View File

@ -726,6 +726,35 @@ namespace Bit.SqliteMigrations.Migrations
b.ToTable("ClientOrganizationMigrationRecord", (string)null);
});
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationInstallation", b =>
{
b.Property<Guid>("Id")
.HasColumnType("TEXT");
b.Property<DateTime>("CreationDate")
.HasColumnType("TEXT");
b.Property<Guid>("InstallationId")
.HasColumnType("TEXT");
b.Property<Guid>("OrganizationId")
.HasColumnType("TEXT");
b.Property<DateTime?>("RevisionDate")
.HasColumnType("TEXT");
b.HasKey("Id")
.HasAnnotation("SqlServer:Clustered", true);
b.HasIndex("InstallationId")
.HasAnnotation("SqlServer:Clustered", false);
b.HasIndex("OrganizationId")
.HasAnnotation("SqlServer:Clustered", false);
b.ToTable("OrganizationInstallation", (string)null);
});
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderInvoiceItem", b =>
{
b.Property<Guid>("Id")
@ -2325,6 +2354,25 @@ namespace Bit.SqliteMigrations.Migrations
b.Navigation("User");
});
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationInstallation", b =>
{
b.HasOne("Bit.Infrastructure.EntityFramework.Models.Installation", "Installation")
.WithMany()
.HasForeignKey("InstallationId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")
.WithMany()
.HasForeignKey("OrganizationId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Installation");
b.Navigation("Organization");
});
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderInvoiceItem", b =>
{
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider")