mirror of
https://github.com/bitwarden/server.git
synced 2026-01-31 14:13:18 +08:00
[PM-21918] update send api models to support new email field (#5895)
* update send api models to support new `email` field
* normalize authentication field evaluation order
* document send response converters
* add FIXME to remove unused constructor argument
* add FIXME to remove unused constructor argument
* introduce `tools-send-email-otp-listing` feature flag
* add `ISendOwnerQuery` to dependency graph
* fix broken tests
* added AuthType prop to send related models with test coverage and debt cleanup
* dotnet format
* add migrations
* dotnet format
* make SendsController null safe (tech debt)
* add AuthType col to Sends table, change Emails col length to 4000, and run migrations
* dotnet format
* update SPs to expect AuthType
* include SP updates in migrations
* remove migrations not intended for merge
* Revert "remove migrations not intended for merge"
This reverts commit 7df56e346a.
undo migrations removal
* extract AuthType inference to util method and remove SQLite file
* fix lints
* address review comments
* fix incorrect assignment and adopt SQL conventions
* fix column assignment order in Send_Update.sql
* remove space added to email list
* assign SQL default value of NULL to AuthType
* update SPs to match migration changes
---------
Co-authored-by: Daniel James Smith <2670567+djsmith85@users.noreply.github.com>
Co-authored-by: Alex Dragovich <46065570+itsadrago@users.noreply.github.com>
Co-authored-by: John Harrington <84741727+harr1424@users.noreply.github.com>
This commit is contained in:
142
util/Migrator/DbScripts/2025-12-18_00_SendAuthType.sql
Normal file
142
util/Migrator/DbScripts/2025-12-18_00_SendAuthType.sql
Normal file
@@ -0,0 +1,142 @@
|
||||
IF COL_LENGTH('[dbo].[Send]', 'AuthType') IS NULL
|
||||
BEGIN
|
||||
ALTER TABLE [dbo].[Send]
|
||||
ADD [AuthType] TINYINT NULL;
|
||||
END
|
||||
GO
|
||||
|
||||
-- Update Send_Create to include AuthType parameter
|
||||
CREATE OR ALTER PROCEDURE [dbo].[Send_Create]
|
||||
@Id UNIQUEIDENTIFIER OUTPUT,
|
||||
@UserId UNIQUEIDENTIFIER,
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@Type TINYINT,
|
||||
@Data VARCHAR(MAX),
|
||||
@Key VARCHAR(MAX),
|
||||
@Password NVARCHAR(300),
|
||||
@MaxAccessCount INT,
|
||||
@AccessCount INT,
|
||||
@CreationDate DATETIME2(7),
|
||||
@RevisionDate DATETIME2(7),
|
||||
@ExpirationDate DATETIME2(7),
|
||||
@DeletionDate DATETIME2(7),
|
||||
@Disabled BIT,
|
||||
@HideEmail BIT,
|
||||
@CipherId UNIQUEIDENTIFIER = NULL,
|
||||
@Emails NVARCHAR(1024) = NULL,
|
||||
@AuthType TINYINT = NULL
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
INSERT INTO [dbo].[Send]
|
||||
(
|
||||
[Id],
|
||||
[UserId],
|
||||
[OrganizationId],
|
||||
[Type],
|
||||
[Data],
|
||||
[Key],
|
||||
[Password],
|
||||
[MaxAccessCount],
|
||||
[AccessCount],
|
||||
[CreationDate],
|
||||
[RevisionDate],
|
||||
[ExpirationDate],
|
||||
[DeletionDate],
|
||||
[Disabled],
|
||||
[HideEmail],
|
||||
[CipherId],
|
||||
[Emails],
|
||||
[AuthType]
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
@Id,
|
||||
@UserId,
|
||||
@OrganizationId,
|
||||
@Type,
|
||||
@Data,
|
||||
@Key,
|
||||
@Password,
|
||||
@MaxAccessCount,
|
||||
@AccessCount,
|
||||
@CreationDate,
|
||||
@RevisionDate,
|
||||
@ExpirationDate,
|
||||
@DeletionDate,
|
||||
@Disabled,
|
||||
@HideEmail,
|
||||
@CipherId,
|
||||
@Emails,
|
||||
@AuthType
|
||||
)
|
||||
|
||||
IF @UserId IS NOT NULL
|
||||
BEGIN
|
||||
IF @Type = 1 --File
|
||||
BEGIN
|
||||
EXEC [dbo].[User_UpdateStorage] @UserId
|
||||
END
|
||||
EXEC [dbo].[User_BumpAccountRevisionDate] @UserId
|
||||
END
|
||||
-- TODO: OrganizationId bump?
|
||||
END
|
||||
GO
|
||||
|
||||
-- Update Send_Update to include AuthType parameter
|
||||
CREATE OR ALTER PROCEDURE [dbo].[Send_Update]
|
||||
@Id UNIQUEIDENTIFIER,
|
||||
@UserId UNIQUEIDENTIFIER,
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@Type TINYINT,
|
||||
@Data VARCHAR(MAX),
|
||||
@Key VARCHAR(MAX),
|
||||
@Password NVARCHAR(300),
|
||||
@MaxAccessCount INT,
|
||||
@AccessCount INT,
|
||||
@CreationDate DATETIME2(7),
|
||||
@RevisionDate DATETIME2(7),
|
||||
@ExpirationDate DATETIME2(7),
|
||||
@DeletionDate DATETIME2(7),
|
||||
@Disabled BIT,
|
||||
@HideEmail BIT,
|
||||
@CipherId UNIQUEIDENTIFIER = NULL,
|
||||
@Emails NVARCHAR(1024) = NULL,
|
||||
@AuthType TINYINT = NULL
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
UPDATE
|
||||
[dbo].[Send]
|
||||
SET
|
||||
[UserId] = @UserId,
|
||||
[OrganizationId] = @OrganizationId,
|
||||
[Type] = @Type,
|
||||
[Data] = @Data,
|
||||
[Key] = @Key,
|
||||
[Password] = @Password,
|
||||
[MaxAccessCount] = @MaxAccessCount,
|
||||
[AccessCount] = @AccessCount,
|
||||
[CreationDate] = @CreationDate,
|
||||
[RevisionDate] = @RevisionDate,
|
||||
[ExpirationDate] = @ExpirationDate,
|
||||
[DeletionDate] = @DeletionDate,
|
||||
[Disabled] = @Disabled,
|
||||
[HideEmail] = @HideEmail,
|
||||
[CipherId] = @CipherId,
|
||||
[Emails] = @Emails,
|
||||
[AuthType] = @AuthType
|
||||
WHERE
|
||||
[Id] = @Id
|
||||
|
||||
IF @UserId IS NOT NULL
|
||||
BEGIN
|
||||
EXEC [dbo].[User_BumpAccountRevisionDate] @UserId
|
||||
END
|
||||
-- TODO: OrganizationId bump?
|
||||
END
|
||||
GO
|
||||
EXECUTE sp_refreshview N'[dbo].[SendView]'
|
||||
GO
|
||||
146
util/Migrator/DbScripts/2025-12-18_01_SendEmailsLength.sql
Normal file
146
util/Migrator/DbScripts/2025-12-18_01_SendEmailsLength.sql
Normal file
@@ -0,0 +1,146 @@
|
||||
IF EXISTS (
|
||||
SELECT *
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE COLUMN_NAME = 'Emails' AND
|
||||
DATA_TYPE = 'nvarchar' AND
|
||||
TABLE_NAME = 'Send' AND
|
||||
CHARACTER_MAXIMUM_LENGTH = 1024)
|
||||
BEGIN
|
||||
ALTER TABLE [dbo].[Send]
|
||||
ALTER COLUMN [Emails] NVARCHAR (4000) NULL
|
||||
END
|
||||
GO
|
||||
|
||||
-- Update Send_Create to use NVARCHAR(4000) for Emails
|
||||
CREATE OR ALTER PROCEDURE [dbo].[Send_Create]
|
||||
@Id UNIQUEIDENTIFIER OUTPUT,
|
||||
@UserId UNIQUEIDENTIFIER,
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@Type TINYINT,
|
||||
@Data VARCHAR(MAX),
|
||||
@Key VARCHAR(MAX),
|
||||
@Password NVARCHAR(300),
|
||||
@MaxAccessCount INT,
|
||||
@AccessCount INT,
|
||||
@CreationDate DATETIME2(7),
|
||||
@RevisionDate DATETIME2(7),
|
||||
@ExpirationDate DATETIME2(7),
|
||||
@DeletionDate DATETIME2(7),
|
||||
@Disabled BIT,
|
||||
@HideEmail BIT,
|
||||
@CipherId UNIQUEIDENTIFIER = NULL,
|
||||
@Emails NVARCHAR(4000) = NULL,
|
||||
@AuthType TINYINT = NULL
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
INSERT INTO [dbo].[Send]
|
||||
(
|
||||
[Id],
|
||||
[UserId],
|
||||
[OrganizationId],
|
||||
[Type],
|
||||
[Data],
|
||||
[Key],
|
||||
[Password],
|
||||
[MaxAccessCount],
|
||||
[AccessCount],
|
||||
[CreationDate],
|
||||
[RevisionDate],
|
||||
[ExpirationDate],
|
||||
[DeletionDate],
|
||||
[Disabled],
|
||||
[HideEmail],
|
||||
[CipherId],
|
||||
[Emails],
|
||||
[AuthType]
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
@Id,
|
||||
@UserId,
|
||||
@OrganizationId,
|
||||
@Type,
|
||||
@Data,
|
||||
@Key,
|
||||
@Password,
|
||||
@MaxAccessCount,
|
||||
@AccessCount,
|
||||
@CreationDate,
|
||||
@RevisionDate,
|
||||
@ExpirationDate,
|
||||
@DeletionDate,
|
||||
@Disabled,
|
||||
@HideEmail,
|
||||
@CipherId,
|
||||
@Emails,
|
||||
@AuthType
|
||||
)
|
||||
|
||||
IF @UserId IS NOT NULL
|
||||
BEGIN
|
||||
IF @Type = 1 --File
|
||||
BEGIN
|
||||
EXEC [dbo].[User_UpdateStorage] @UserId
|
||||
END
|
||||
EXEC [dbo].[User_BumpAccountRevisionDate] @UserId
|
||||
END
|
||||
-- TODO: OrganizationId bump?
|
||||
END
|
||||
GO
|
||||
|
||||
-- Update Send_Update to use NVARCHAR(4000) for Emails
|
||||
CREATE OR ALTER PROCEDURE [dbo].[Send_Update]
|
||||
@Id UNIQUEIDENTIFIER,
|
||||
@UserId UNIQUEIDENTIFIER,
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@Type TINYINT,
|
||||
@Data VARCHAR(MAX),
|
||||
@Key VARCHAR(MAX),
|
||||
@Password NVARCHAR(300),
|
||||
@MaxAccessCount INT,
|
||||
@AccessCount INT,
|
||||
@CreationDate DATETIME2(7),
|
||||
@RevisionDate DATETIME2(7),
|
||||
@ExpirationDate DATETIME2(7),
|
||||
@DeletionDate DATETIME2(7),
|
||||
@Disabled BIT,
|
||||
@HideEmail BIT,
|
||||
@CipherId UNIQUEIDENTIFIER = NULL,
|
||||
@Emails NVARCHAR(4000) = NULL,
|
||||
@AuthType TINYINT = NULL
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
UPDATE
|
||||
[dbo].[Send]
|
||||
SET
|
||||
[UserId] = @UserId,
|
||||
[OrganizationId] = @OrganizationId,
|
||||
[Type] = @Type,
|
||||
[Data] = @Data,
|
||||
[Key] = @Key,
|
||||
[Password] = @Password,
|
||||
[MaxAccessCount] = @MaxAccessCount,
|
||||
[AccessCount] = @AccessCount,
|
||||
[CreationDate] = @CreationDate,
|
||||
[RevisionDate] = @RevisionDate,
|
||||
[ExpirationDate] = @ExpirationDate,
|
||||
[DeletionDate] = @DeletionDate,
|
||||
[Disabled] = @Disabled,
|
||||
[HideEmail] = @HideEmail,
|
||||
[CipherId] = @CipherId,
|
||||
[Emails] = @Emails,
|
||||
[AuthType] = @AuthType
|
||||
WHERE
|
||||
[Id] = @Id
|
||||
|
||||
IF @UserId IS NOT NULL
|
||||
BEGIN
|
||||
EXEC [dbo].[User_BumpAccountRevisionDate] @UserId
|
||||
END
|
||||
-- TODO: OrganizationId bump?
|
||||
END
|
||||
GO
|
||||
3446
util/MySqlMigrations/Migrations/20251217190832_AddAuthTypeToSend.Designer.cs
generated
Normal file
3446
util/MySqlMigrations/Migrations/20251217190832_AddAuthTypeToSend.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.MySqlMigrations.Migrations;
|
||||
|
||||
/// <inheritdoc />
|
||||
public partial class AddAuthTypeToSend : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<byte>(
|
||||
name: "AuthType",
|
||||
table: "Send",
|
||||
type: "tinyint unsigned",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AuthType",
|
||||
table: "Send");
|
||||
}
|
||||
}
|
||||
3446
util/MySqlMigrations/Migrations/20251218153214_SendAuthTypeAndEmailLength.Designer.cs
generated
Normal file
3446
util/MySqlMigrations/Migrations/20251218153214_SendAuthTypeAndEmailLength.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,43 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.MySqlMigrations.Migrations;
|
||||
|
||||
/// <inheritdoc />
|
||||
public partial class SendAuthTypeAndEmailLength : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Emails",
|
||||
table: "Send",
|
||||
type: "varchar(4000)",
|
||||
maxLength: 4000,
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "varchar(1024)",
|
||||
oldMaxLength: 1024,
|
||||
oldNullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Emails",
|
||||
table: "Send",
|
||||
type: "varchar(1024)",
|
||||
maxLength: 1024,
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "varchar(4000)",
|
||||
oldMaxLength: 4000,
|
||||
oldNullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
}
|
||||
@@ -1632,6 +1632,9 @@ namespace Bit.MySqlMigrations.Migrations
|
||||
b.Property<int>("AccessCount")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<byte?>("AuthType")
|
||||
.HasColumnType("tinyint unsigned");
|
||||
|
||||
b.Property<Guid?>("CipherId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
@@ -1648,8 +1651,8 @@ namespace Bit.MySqlMigrations.Migrations
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("Emails")
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("varchar(1024)");
|
||||
.HasMaxLength(4000)
|
||||
.HasColumnType("varchar(4000)");
|
||||
|
||||
b.Property<DateTime?>("ExpirationDate")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
3452
util/PostgresMigrations/Migrations/20251217190840_AddAuthTypeToSend.Designer.cs
generated
Normal file
3452
util/PostgresMigrations/Migrations/20251217190840_AddAuthTypeToSend.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.PostgresMigrations.Migrations;
|
||||
|
||||
/// <inheritdoc />
|
||||
public partial class AddAuthTypeToSend : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<byte>(
|
||||
name: "AuthType",
|
||||
table: "Send",
|
||||
type: "smallint",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AuthType",
|
||||
table: "Send");
|
||||
}
|
||||
}
|
||||
3452
util/PostgresMigrations/Migrations/20251218153210_SendAuthTypeAndEmailLength.Designer.cs
generated
Normal file
3452
util/PostgresMigrations/Migrations/20251218153210_SendAuthTypeAndEmailLength.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,39 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.PostgresMigrations.Migrations;
|
||||
|
||||
/// <inheritdoc />
|
||||
public partial class SendAuthTypeAndEmailLength : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Emails",
|
||||
table: "Send",
|
||||
type: "character varying(4000)",
|
||||
maxLength: 4000,
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "character varying(1024)",
|
||||
oldMaxLength: 1024,
|
||||
oldNullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Emails",
|
||||
table: "Send",
|
||||
type: "character varying(1024)",
|
||||
maxLength: 1024,
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "character varying(4000)",
|
||||
oldMaxLength: 4000,
|
||||
oldNullable: true);
|
||||
}
|
||||
}
|
||||
@@ -1637,6 +1637,9 @@ namespace Bit.PostgresMigrations.Migrations
|
||||
b.Property<int>("AccessCount")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<byte?>("AuthType")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<Guid?>("CipherId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
@@ -1653,8 +1656,8 @@ namespace Bit.PostgresMigrations.Migrations
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("Emails")
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("character varying(1024)");
|
||||
.HasMaxLength(4000)
|
||||
.HasColumnType("character varying(4000)");
|
||||
|
||||
b.Property<DateTime?>("ExpirationDate")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
3435
util/SqliteMigrations/Migrations/20251217190836_AddAuthTypeToSend.Designer.cs
generated
Normal file
3435
util/SqliteMigrations/Migrations/20251217190836_AddAuthTypeToSend.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.SqliteMigrations.Migrations;
|
||||
|
||||
/// <inheritdoc />
|
||||
public partial class AddAuthTypeToSend : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<byte>(
|
||||
name: "AuthType",
|
||||
table: "Send",
|
||||
type: "INTEGER",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AuthType",
|
||||
table: "Send");
|
||||
}
|
||||
}
|
||||
3435
util/SqliteMigrations/Migrations/20251218153206_SendAuthTypeAndEmailLength.Designer.cs
generated
Normal file
3435
util/SqliteMigrations/Migrations/20251218153206_SendAuthTypeAndEmailLength.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.SqliteMigrations.Migrations;
|
||||
|
||||
/// <inheritdoc />
|
||||
public partial class SendAuthTypeAndEmailLength : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1621,6 +1621,9 @@ namespace Bit.SqliteMigrations.Migrations
|
||||
b.Property<int>("AccessCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<byte?>("AuthType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid?>("CipherId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@@ -1637,7 +1640,7 @@ namespace Bit.SqliteMigrations.Migrations
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Emails")
|
||||
.HasMaxLength(1024)
|
||||
.HasMaxLength(4000)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("ExpirationDate")
|
||||
|
||||
Reference in New Issue
Block a user