This commit is contained in:
eraden 2023-06-11 16:21:13 +02:00
parent b9c017b1fa
commit da2c1b9b19
18 changed files with 873 additions and 670 deletions

15
Cargo.lock generated
View File

@ -2763,6 +2763,8 @@ dependencies = [
"console",
"lazy_static",
"linked-hash-map",
"ron",
"serde",
"similar",
"yaml-rust",
]
@ -3282,7 +3284,9 @@ dependencies = [
"async-trait",
"clap 3.2.25",
"dotenv",
"insta",
"sea-orm-migration",
"sqlx",
"tracing",
"tracing-subscriber",
]
@ -4592,6 +4596,17 @@ dependencies = [
"serde",
]
[[package]]
name = "ron"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a"
dependencies = [
"base64 0.13.1",
"bitflags",
"serde",
]
[[package]]
name = "rumqttc"
version = "0.21.0"

View File

@ -19,5 +19,10 @@ async-trait = { version = "0.1.68" }
[dependencies.sea-orm-migration]
version = "0.11.0"
features = [
"sqlx-postgres"
"sqlx-postgres",
'runtime-tokio-rustls'
]
[dev-dependencies]
insta = { version = "1.29.0", features = ['ron'] }
sqlx = { version = "0.6.3", features = ['runtime-tokio-rustls'] }

View File

@ -1,31 +1,49 @@
use sea_orm_migration::prelude::*;
use sea_query::expr::SimpleExpr;
/// ```sql
/// id character varying NOT NULL,
/// email character varying,
/// billing_address_id character varying,
/// shipping_address_id character varying,
/// region_id character varying NOT NULL,
/// customer_id character varying,
/// payment_id character varying,
/// type public.cart_types DEFAULT 'default'::public.cart_types NOT NULL,
/// completed_at timestamp with time zone,
/// created_at timestamp with time zone DEFAULT now() NOT NULL,
/// updated_at timestamp with time zone DEFAULT now() NOT NULL,
/// deleted_at timestamp with time zone,
/// metadata jsonb,
/// idempotency_key character varying,
/// context jsonb,
/// payment_authorized_at timestamp with time zone,
/// sales_channel_id character varying
/// ```
use crate::sea_orm::Iterable;
use crate::{ts_def_now_not_null, DropTable, IntoColumnDef};
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
async fn up(&self, m: &SchemaManager) -> Result<(), DbErr> {
Self::create_carts(m).await?;
Ok(())
}
async fn down(&self, m: &SchemaManager) -> Result<(), DbErr> {
self.drop_table(m, Cart::Carts).await?;
Ok(())
}
}
impl Migration {
/// ```sql
/// CREATE TABLE carts
/// (
/// id uuid NOT NULL,
/// email character varying,
/// billing_address_id uuid,
/// shipping_address_id uuid,
/// region_id uuid NOT NULL,
/// customer_id uuid,
/// payment_id uuid,
/// type cart_types DEFAULT 'default'::cart_types NOT NULL,
/// completed_at timestamp with time zone,
/// created_at timestamp with time zone DEFAULT now() NOT NULL,
/// updated_at timestamp with time zone DEFAULT now() NOT NULL,
/// deleted_at timestamp with time zone,
/// metadata jsonb,
/// idempotency_key character varying,
/// context jsonb,
/// payment_authorized_at timestamp with time zone,
/// sales_channel_id uuid
/// );
/// ```
async fn create_carts(manager: &SchemaManager<'_>) -> Result<(), DbErr> {
manager
.create_table(
Table::create()
@ -37,48 +55,35 @@ impl MigrationTrait for Migration {
.default(SimpleExpr::Custom("public.uuid_generate_v4()".into()))
.primary_key(),
)
.col(ColumnDef::new(Cart::Email).string())
.col(ColumnDef::new(Cart::BillingAddressId).uuid())
.col(ColumnDef::new(Cart::ShippingAddressId).uuid())
.col(ColumnDef::new(Cart::RegionId).uuid().not_null())
.col(ColumnDef::new(Cart::CustomerId).uuid())
.col(ColumnDef::new(Cart::PaymentId).uuid())
.col(Cart::Email.col().string())
.col(Cart::BillingAddressId.col().uuid())
.col(Cart::ShippingAddressId.col().uuid())
.col(Cart::RegionId.col().uuid().not_null())
.col(Cart::CustomerId.col().uuid())
.col(Cart::PaymentId.col().uuid())
.col(
ColumnDef::new(Cart::CartType)
.string()
.default(SimpleExpr::Custom("'default'::public.cart_types".into()))
Cart::CartType
.col()
.enumeration(
crate::types::CartType::CartTypes,
crate::types::CartType::iter().skip(1),
)
.default(crate::types::CartType::Default.to_string())
.not_null(),
)
.col(ColumnDef::new(Cart::CompletedAt).timestamp())
.col(
ColumnDef::new(Cart::CreatedAt)
.timestamp()
.default(SimpleExpr::Custom("now()".into()))
.not_null(),
)
.col(
ColumnDef::new(Cart::UpdatedAt)
.timestamp()
.default(SimpleExpr::Custom("now()".into()))
.not_null(),
)
.col(ColumnDef::new(Cart::DeletedAt).timestamp())
.col(ColumnDef::new(Cart::Metadata).json_binary())
.col(ColumnDef::new(Cart::IdempotencyKey).uuid())
.col(ColumnDef::new(Cart::Context).json_binary())
.col(ColumnDef::new(Cart::PaymentAuthorizedAt).timestamp())
.col(ColumnDef::new(Cart::SalesChannelId).uuid())
.col(Cart::CompletedAt.col().timestamp())
.col(ts_def_now_not_null!(Cart::CreatedAt))
.col(ts_def_now_not_null!(Cart::UpdatedAt))
.col(Cart::DeletedAt.col().timestamp())
.col(Cart::Metadata.col().json_binary())
.col(Cart::IdempotencyKey.col().uuid())
.col(Cart::Context.col().json_binary())
.col(Cart::PaymentAuthorizedAt.col().timestamp())
.col(Cart::SalesChannelId.col().uuid())
.to_owned(),
)
.await
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(Cart::Carts).to_owned())
.await?;
Ok(())
}
}
#[derive(Iden)]

View File

@ -1,5 +1,7 @@
use sea_orm_migration::prelude::*;
use crate::{to_pk2_name, CreateIndexExt, DropTable, IntoColumnDef};
/// ```sql
/// CREATE TABLE cart_discounts
/// (
@ -17,47 +19,94 @@ pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.create_table(
Table::create()
.table(CartDiscount::CartDiscounts)
.col(ColumnDef::new(CartDiscount::CartId).uuid().not_null())
.col(ColumnDef::new(CartDiscount::DiscountId).uuid().not_null())
.to_owned(),
)
.await?;
manager
.create_table(
Table::create()
.table(CartGiftCard::CartGiftCards)
.col(ColumnDef::new(CartGiftCard::CartId).uuid().not_null())
.col(ColumnDef::new(CartGiftCard::GiftCardId).uuid().not_null())
.to_owned(),
)
.await?;
async fn up(&self, m: &SchemaManager) -> Result<(), DbErr> {
Self::create_cart_discounts(m).await?;
Self::create_cart_gift_cards(m).await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(CartDiscount::CartDiscounts).to_owned())
.await?;
manager
.drop_table(Table::drop().table(CartGiftCard::CartGiftCards).to_owned())
.await?;
async fn down(&self, m: &SchemaManager) -> Result<(), DbErr> {
self.drop_table(m, CartDiscount::CartDiscounts).await?;
self.drop_table(m, CartGiftCard::CartGiftCards).await?;
Ok(())
}
}
#[derive(Iden)]
impl Migration {
/// ```sql
/// CREATE TABLE cart_discounts
/// (
/// cart_id uuid NOT NULL,
/// discount_id uuid NOT NULL
/// );
/// ```
async fn create_cart_discounts(m: &SchemaManager<'_>) -> Result<(), DbErr> {
m.create_table(
Table::create()
.if_not_exists()
.table(CartDiscount::CartDiscounts)
.col(CartDiscount::CartId.col().uuid().not_null())
.col(CartDiscount::DiscountId.col().uuid().not_null())
.to_owned(),
)
.await?;
m.create_2col_idx(
CartDiscount::CartDiscounts,
CartDiscount::CartId,
CartDiscount::DiscountId,
)
.await?;
Ok(())
}
/// ```sql
/// CREATE TABLE cart_gift_cards
/// (
/// cart_id uuid NOT NULL,
/// gift_card_id uuid NOT NULL
/// );
/// ```
async fn create_cart_gift_cards(m: &SchemaManager<'_>) -> Result<(), DbErr> {
m.create_table(
Table::create()
.table(CartGiftCard::CartGiftCards)
.if_not_exists()
.col(CartGiftCard::CartId.col().uuid().not_null())
.col(CartGiftCard::GiftCardId.col().uuid().not_null())
.to_owned(),
)
.await?;
m.create_index(
IndexCreateStatement::new()
.table(CartGiftCard::CartGiftCards)
.col(CartGiftCard::CartId)
.col(CartGiftCard::GiftCardId)
.name(&to_pk2_name(
CartGiftCard::CartGiftCards,
CartGiftCard::CartId,
CartGiftCard::GiftCardId,
))
.primary()
.if_not_exists()
.to_owned(),
)
.await?;
Ok(())
}
}
#[derive(Iden, Clone, Copy)]
enum CartDiscount {
CartDiscounts,
CartId,
DiscountId,
}
#[derive(Iden)]
#[derive(Iden, Copy, Clone)]
enum CartGiftCard {
CartGiftCards,
CartId,

View File

@ -3,7 +3,7 @@ use sea_orm_migration::prelude::*;
use crate::sea_orm::Iterable;
use crate::{
auto_uuid_not_null, create_type, drop_type, to_fk_name, to_pk2_name, ts_def_now_not_null,
DropTable, IntoColumnDef,
CreateIndexExt, DropTable, IntoColumnDef,
};
#[derive(DeriveMigrationName)]
@ -122,9 +122,11 @@ impl Migration {
m.create_table(
Table::create()
.table(Order::Orders)
.if_not_exists()
.col(auto_uuid_not_null!(Order::Id))
.col(
ColumnDef::new(Order::Status)
Order::Status
.col()
.enumeration(
crate::types::OrderStatus::OrderStatuses,
crate::types::OrderStatus::iter().skip(1),
@ -133,7 +135,8 @@ impl Migration {
.not_null(),
)
.col(
ColumnDef::new(Order::FulfillmentStatus)
Order::FulfillmentStatus
.col()
.enumeration(
crate::types::OrderFulfillmentStatus::OrderFulfillmentStatuses,
crate::types::OrderFulfillmentStatus::iter().take(1),
@ -141,31 +144,32 @@ impl Migration {
.not_null(),
)
.col(
ColumnDef::new(Order::PaymentStatus)
Order::PaymentStatus
.col()
.enumeration(
crate::types::OrderPaymentStatus::OrderPaymentStatuses,
crate::types::OrderPaymentStatus::iter().take(1),
)
.not_null(),
)
.col(ColumnDef::new(Order::DisplayId).uuid())
.col(ColumnDef::new(Order::CartId).uuid())
.col(ColumnDef::new(Order::CustomerId).uuid())
.col(ColumnDef::new(Order::Email).uuid())
.col(ColumnDef::new(Order::BillingAddressId).uuid())
.col(ColumnDef::new(Order::ShippingAddressId).uuid())
.col(ColumnDef::new(Order::RegionId).uuid())
.col(ColumnDef::new(Order::CurrencyCode).uuid())
.col(ColumnDef::new(Order::TaxRate).uuid())
.col(ColumnDef::new(Order::CanceledAt).uuid())
.col(ColumnDef::new(Order::CreatedAt).uuid())
.col(ColumnDef::new(Order::UpdatedAt).uuid())
.col(ColumnDef::new(Order::Metadata).uuid())
.col(ColumnDef::new(Order::IdempotencyKey).uuid())
.col(ColumnDef::new(Order::DraftOrderId).uuid())
.col(ColumnDef::new(Order::NoNotification).uuid())
.col(ColumnDef::new(Order::ExternalId).uuid())
.col(ColumnDef::new(Order::SalesChannelId).uuid())
.col(Order::DisplayId.col().uuid())
.col(Order::CartId.col().uuid())
.col(Order::CustomerId.col().uuid())
.col(Order::Email.col().uuid())
.col(Order::BillingAddressId.col().uuid())
.col(Order::ShippingAddressId.col().uuid())
.col(Order::RegionId.col().uuid())
.col(Order::CurrencyCode.col().uuid())
.col(Order::TaxRate.col().uuid())
.col(Order::CanceledAt.col().uuid())
.col(Order::CreatedAt.col().uuid())
.col(Order::UpdatedAt.col().uuid())
.col(Order::Metadata.col().uuid())
.col(Order::IdempotencyKey.col().uuid())
.col(Order::DraftOrderId.col().uuid())
.col(Order::NoNotification.col().uuid())
.col(Order::ExternalId.col().uuid())
.col(Order::SalesChannelId.col().uuid())
.to_owned(),
)
.await?;
@ -183,11 +187,20 @@ impl Migration {
m.create_table(
Table::create()
.table(OrderDiscount::OrderDiscounts)
.col(ColumnDef::new(OrderDiscount::OrderId).uuid().not_null())
.col(ColumnDef::new(OrderDiscount::DiscountId).uuid().not_null())
.if_not_exists()
.col(OrderDiscount::OrderId.col().uuid().not_null())
.col(OrderDiscount::DiscountId.col().uuid().not_null())
.to_owned(),
)
.await?;
m.create_2col_idx(
OrderDiscount::OrderDiscounts,
OrderDiscount::OrderId,
OrderDiscount::DiscountId,
)
.await?;
Ok(())
}
@ -216,22 +229,23 @@ impl Migration {
m.create_table(
Table::create()
.table(OrderEdit::OrderEdits)
.if_not_exists()
.col(auto_uuid_not_null!(OrderEdit::Id))
.col(ts_def_now_not_null!(OrderEdit::CreatedAt))
.col(ts_def_now_not_null!(OrderEdit::UpdatedAt))
.col(ColumnDef::new(OrderEdit::OrderId).uuid().not_null())
.col(ColumnDef::new(OrderEdit::InternalNote).string())
.col(ColumnDef::new(OrderEdit::CreatedBy).uuid().not_null())
.col(ColumnDef::new(OrderEdit::RequestedBy).uuid())
.col(ColumnDef::new(OrderEdit::RequestedAt).timestamp())
.col(ColumnDef::new(OrderEdit::ConfirmedBy).uuid())
.col(ColumnDef::new(OrderEdit::ConfirmedAt).timestamp())
.col(ColumnDef::new(OrderEdit::DeclinedBy).uuid())
.col(ColumnDef::new(OrderEdit::DeclinedReason).string())
.col(ColumnDef::new(OrderEdit::DeclinedAt).timestamp())
.col(ColumnDef::new(OrderEdit::CanceledBy).uuid())
.col(ColumnDef::new(OrderEdit::CanceledAt).timestamp())
.col(ColumnDef::new(OrderEdit::PaymentCollectionId).uuid())
.col(OrderEdit::OrderId.col().uuid().not_null())
.col(OrderEdit::InternalNote.col().string())
.col(OrderEdit::CreatedBy.col().uuid().not_null())
.col(OrderEdit::RequestedBy.col().uuid())
.col(OrderEdit::RequestedAt.col().timestamp())
.col(OrderEdit::ConfirmedBy.col().uuid())
.col(OrderEdit::ConfirmedAt.col().timestamp())
.col(OrderEdit::DeclinedBy.col().uuid())
.col(OrderEdit::DeclinedReason.col().string())
.col(OrderEdit::DeclinedAt.col().timestamp())
.col(OrderEdit::CanceledBy.col().uuid())
.col(OrderEdit::CanceledAt.col().timestamp())
.col(OrderEdit::PaymentCollectionId.col().uuid())
.to_owned(),
)
.await?;
@ -249,11 +263,20 @@ impl Migration {
m.create_table(
Table::create()
.table(OrderGiftCard::OrderGiftCards)
.col(ColumnDef::new(OrderGiftCard::OrderId).uuid().not_null())
.col(ColumnDef::new(OrderGiftCard::GiftCardId).uuid().not_null())
.if_not_exists()
.col(OrderGiftCard::OrderId.col().uuid().not_null())
.col(OrderGiftCard::GiftCardId.col().uuid().not_null())
.to_owned(),
)
.await?;
m.create_2col_idx(
OrderGiftCard::OrderGiftCards,
OrderGiftCard::OrderId,
OrderGiftCard::GiftCardId,
)
.await?;
Ok(())
}
@ -421,31 +444,20 @@ impl Migration {
Table::create()
.table(PaymentCollectionPayment::PaymentCollectionPayments)
.col(
ColumnDef::new(PaymentCollectionPayment::PaymentCollectionId)
.uuid()
.not_null(),
)
.col(
ColumnDef::new(PaymentCollectionPayment::PaymentId)
PaymentCollectionPayment::PaymentCollectionId
.col()
.uuid()
.not_null(),
)
.col(PaymentCollectionPayment::PaymentId.col().uuid().not_null())
.to_owned(),
)
.await?;
m.create_index(
IndexCreateStatement::new()
.table(PaymentCollectionPayment::PaymentCollectionPayments)
.col(PaymentCollectionPayment::PaymentCollectionId)
.col(PaymentCollectionPayment::PaymentId)
.primary()
.name(&to_pk2_name(
m.create_2col_idx(
PaymentCollectionPayment::PaymentCollectionPayments,
PaymentCollectionPayment::PaymentCollectionId,
PaymentCollectionPayment::PaymentId,
))
.to_owned(),
)
.await?;
@ -494,18 +506,10 @@ impl Migration {
)
.await?;
m.create_index(
IndexCreateStatement::new()
.table(PaymentCollectionSession::PaymentCollectionSessions)
.col(PaymentCollectionSession::PaymentCollectionId)
.col(PaymentCollectionSession::PaymentSessionId)
.primary()
.name(&to_pk2_name(
m.create_2col_idx(
PaymentCollectionSession::PaymentCollectionSessions,
PaymentCollectionSession::PaymentCollectionId,
PaymentCollectionSession::PaymentSessionId,
))
.to_owned(),
)
.await?;
Ok(())
@ -589,7 +593,7 @@ impl Migration {
}
}
#[derive(Iden)]
#[derive(Iden, Copy, Clone)]
pub enum Order {
Orders,
Id,
@ -616,14 +620,14 @@ pub enum Order {
SalesChannelId,
}
#[derive(Iden)]
#[derive(Iden, Copy, Clone)]
pub enum OrderDiscount {
OrderDiscounts,
OrderId,
DiscountId,
}
#[derive(Iden)]
#[derive(Iden, Copy, Clone)]
pub enum OrderEdit {
OrderEdits,
Id,
@ -644,14 +648,14 @@ pub enum OrderEdit {
PaymentCollectionId,
}
#[derive(Iden)]
#[derive(Iden, Copy, Clone)]
pub enum OrderGiftCard {
OrderGiftCards,
OrderId,
GiftCardId,
}
#[derive(Iden)]
#[derive(Iden, Copy, Clone)]
pub enum OrderItemChange {
OrderItemChanges,
Id,
@ -664,7 +668,7 @@ pub enum OrderItemChange {
LineItemId,
}
#[derive(Iden)]
#[derive(Iden, Copy, Clone)]
pub enum Payment {
Payments,
Id,
@ -702,21 +706,21 @@ pub enum PaymentCollection {
CreatedBy,
}
#[derive(Iden)]
#[derive(Iden, Copy, Clone)]
pub enum PaymentCollectionPayment {
PaymentCollectionPayments,
PaymentCollectionId,
PaymentId,
}
#[derive(Iden)]
#[derive(Iden, Copy, Clone)]
pub enum PaymentCollectionSession {
PaymentCollectionSessions,
PaymentCollectionId,
PaymentSessionId,
}
#[derive(Iden)]
#[derive(Iden, Copy, Clone)]
pub enum PaymentProvider {
PaymentProviders,
Id,

View File

@ -2,8 +2,8 @@ use sea_orm_migration::prelude::*;
use crate::constraint::Check;
use crate::{
auto_uuid_not_null, create_constraint, create_type, drop_type, to_fk_name, to_pk2_name,
ts_def_now_not_null, AsIden,
auto_uuid_not_null, create_type, drop_type, to_fk_name, to_pk2_name, ts_def_now_not_null,
AsIden, CreateConstraint,
};
#[derive(DeriveMigrationName)]
@ -55,20 +55,30 @@ impl Migration {
/// );
/// ```
async fn create_line_items(&self, m: &SchemaManager<'_>) -> Result<(), DbErr> {
create_constraint(
m,
m.create_constraint(
LineItem::LineItems,
Check::lower_eq(LineItem::ShippedQuantity, LineItem::FulfilledQuantity),
Check::less_eq(LineItem::ShippedQuantity, LineItem::FulfilledQuantity),
)
.await?;
create_constraint(
m,
m.create_constraint(
LineItem::LineItems,
Check::greater(LineItem::Quantity, 0.iden()),
)
.await?;
m.create_constraint(
LineItem::LineItems,
Check::less_eq(LineItem::ReturnedQuantity, LineItem::Quantity),
)
.await?;
m.create_constraint(
LineItem::LineItems,
Check::less_eq(LineItem::FulfilledQuantity, LineItem::Quantity),
)
.await?;
Ok(())
}

View File

@ -1,6 +1,6 @@
use sea_orm_migration::prelude::*;
use crate::{auto_uuid_not_null, ts_def_now_not_null, IntoColumnDef};
use crate::{auto_uuid_not_null, ts_def_now_not_null, DropTable, IntoColumnDef};
#[derive(DeriveMigrationName)]
pub struct Migration;
@ -14,13 +14,8 @@ impl MigrationTrait for Migration {
}
async fn down(&self, m: &SchemaManager) -> Result<(), DbErr> {
m.drop_table(Table::drop().table(GiftCard::GiftCards).to_owned())
.await?;
m.drop_table(
Table::drop()
.table(GiftCardTransaction::GiftCardTransactions)
.to_owned(),
)
self.drop_table(m, GiftCard::GiftCards).await?;
self.drop_table(m, GiftCardTransaction::GiftCardTransactions)
.await?;
Ok(())

View File

@ -1,78 +1,68 @@
use sea_orm_migration::prelude::*;
use sea_query::expr::SimpleExpr;
use crate::{auto_uuid_not_null, ts_def_now_not_null, DropTable, IntoColumnDef};
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
Self::create_batch_jobs(manager).await
}
async fn down(&self, m: &SchemaManager) -> Result<(), DbErr> {
self.drop_table(m, BatchJob::BatchJobs).await?;
Ok(())
}
}
impl Migration {
/// ```sql
/// CREATE TABLE batch_jobs
/// (
/// id uuid NOT NULL,
/// type text NOT NULL,
/// created_by character varying,
/// context jsonb,
/// result jsonb,
/// dry_run boolean DEFAULT false NOT NULL,
/// created_at timestamp with time zone DEFAULT now() NOT NULL,
/// pre_processed_at timestamp with time zone,
/// confirmed_at timestamp with time zone,
/// processing_at timestamp with time zone,
/// completed_at timestamp with time zone,
/// failed_at timestamp with time zone,
/// canceled_at timestamp with time zone,
/// updated_at timestamp with time zone DEFAULT now() NOT NULL,
/// deleted_at timestamp with time zone
/// );
/// ```
async fn create_batch_jobs(manager: &SchemaManager<'_>) -> Result<(), DbErr> {
manager
.create_table(
Table::create()
// id character varying NOT NULL,
// type text NOT NULL,
// created_by character varying,
// context jsonb,
// result jsonb,
// dry_run boolean DEFAULT false NOT NULL,
// created_at timestamp with time zone DEFAULT now() NOT NULL,
// pre_processed_at timestamp with time zone,
// confirmed_at timestamp with time zone,
// processing_at timestamp with time zone,
// completed_at timestamp with time zone,
// failed_at timestamp with time zone,
// canceled_at timestamp with time zone,
// updated_at timestamp with time zone DEFAULT now() NOT NULL,
// deleted_at timestamp with time zone
.table(BatchJob::BatchJobs)
.col(
ColumnDef::new(BatchJob::Id)
.uuid()
.not_null()
.default(SimpleExpr::Custom("public.uuid_generate_v4()".into()))
.primary_key(),
)
.col(ColumnDef::new(BatchJob::BatchType).string().not_null())
.col(ColumnDef::new(BatchJob::CreatedBy).uuid())
.col(ColumnDef::new(BatchJob::Context).json_binary())
.col(ColumnDef::new(BatchJob::Result).json_binary())
.col(
ColumnDef::new(BatchJob::DryRun)
.boolean()
.default(false)
.not_null(),
)
.col(
ColumnDef::new(BatchJob::CreatedAt)
.timestamp()
.default(SimpleExpr::Custom("now()".into()))
.not_null(),
)
.col(ColumnDef::new(BatchJob::PreProcessedAt).timestamp())
.col(ColumnDef::new(BatchJob::ConfirmedAt).timestamp())
.col(ColumnDef::new(BatchJob::ProcessingAt).timestamp())
.col(ColumnDef::new(BatchJob::CompletedAt).timestamp())
.col(ColumnDef::new(BatchJob::FailedAt).timestamp())
.col(ColumnDef::new(BatchJob::CanceledAt).timestamp())
.col(
ColumnDef::new(BatchJob::UpdatedAt)
.timestamp()
.default(SimpleExpr::Custom("now()".into()))
.not_null(),
)
.col(ColumnDef::new(BatchJob::DeletedAt).timestamp())
.col(auto_uuid_not_null!(BatchJob::Id))
.col(BatchJob::BatchType.col().string().not_null())
.col(BatchJob::CreatedBy.col().uuid())
.col(BatchJob::Context.col().json_binary())
.col(BatchJob::Result.col().json_binary())
.col(BatchJob::DryRun.col().boolean().default(false).not_null())
.col(ts_def_now_not_null!(BatchJob::CreatedAt))
.col(BatchJob::PreProcessedAt.col().timestamp())
.col(BatchJob::ConfirmedAt.col().timestamp())
.col(BatchJob::ProcessingAt.col().timestamp())
.col(BatchJob::CompletedAt.col().timestamp())
.col(BatchJob::FailedAt.col().timestamp())
.col(BatchJob::CanceledAt.col().timestamp())
.col(ts_def_now_not_null!(BatchJob::UpdatedAt))
.col(BatchJob::DeletedAt.col().timestamp())
.to_owned(),
)
.await
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(BatchJob::BatchJobs).to_owned())
.await?;
Ok(())
}
}
#[derive(Iden)]

View File

@ -1,5 +1,3 @@
use std::fmt::{Display, Formatter};
pub use sea_orm_migration::prelude::*;
pub mod carts;
@ -16,238 +14,8 @@ pub mod checkouts;
pub use checkouts::*;
pub mod shipping;
pub use shipping::*;
mod sea_ext;
pub use sea_ext::*;
pub mod types;
pub use types::*;
use crate::constraint::Check;
#[macro_export]
macro_rules! ts_def_now_not_null {
($identifier: expr) => {
ColumnDef::new($identifier)
.timestamp()
.default(SimpleExpr::Custom("now()".into()))
.not_null()
};
}
#[macro_export]
macro_rules! auto_uuid_not_null {
($identifier: expr) => {
ColumnDef::new($identifier)
.uuid()
.not_null()
.default(SimpleExpr::Custom("public.uuid_generate_v4()".into()))
.primary_key()
};
}
// #[macro_export]
// macro_rules! col {
// ($name: expr $ty: expr) => {
// ColumnDef::new($name).$ty()
// };
// ($name: expr auto uuid) => {
// $crate::auto_uuid_not_null!($name)
// };
// ($name: expr $ty: expr default $value: expr) => {
// $crate::col!($name $ty).default($value)
// };
// }
#[async_trait::async_trait]
pub trait DropTable {
async fn drop_table<I: Iden + 'static>(
&self,
manager: &SchemaManager<'_>,
identifier: I,
) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(identifier).to_owned())
.await
}
}
impl<T: MigrationTrait> DropTable for T {}
pub trait IntoColumnDef: IntoIden {
fn col(self) -> ColumnDef
where
Self: Sized,
{
ColumnDef::new(self)
}
}
impl<T: IntoIden> IntoColumnDef for T {}
pub fn to_pk2_name<T1: IntoIden, C1: IntoIden, C2: IntoIden>(t1: T1, c1: C1, c2: C2) -> String {
let t1 = to_snake(t1.into_iden().to_string());
let c1 = to_snake(c1.into_iden().to_string());
let c2 = to_snake(c2.into_iden().to_string());
format!("pk_{}_{}_{}", t1, c1, c2)
}
pub fn to_fk_name<T1: IntoIden, C1: IntoIden, T2: IntoIden, C2: IntoIden>(
t1: T1,
c1: C1,
t2: T2,
c2: C2,
) -> String {
let t1 = to_snake(t1.into_iden().to_string());
let c1 = to_snake(c1.into_iden().to_string());
let t2 = to_snake(t2.into_iden().to_string());
let c2 = to_snake(c2.into_iden().to_string());
format!("fk_{}_{}_{}_{}", t1, c1, t2, c2)
}
fn to_snake(s: String) -> String {
s.chars().fold(String::new(), |mut m, c| {
if c.is_ascii_uppercase() {
m.push('_');
}
m.push(c.to_ascii_lowercase());
m
})
}
pub mod constraint {
use std::fmt::{Display, Formatter};
use crate::{Constraint, Iden};
pub enum Check {
Lower(Box<dyn Iden>, Box<dyn Iden>),
LowerEq(Box<dyn Iden>, Box<dyn Iden>),
Eq(Box<dyn Iden>, Box<dyn Iden>),
GreaterEq(Box<dyn Iden>, Box<dyn Iden>),
Greater(Box<dyn Iden>, Box<dyn Iden>),
Or(Box<Check>, Box<Check>),
}
impl Check {
pub fn or(self, r: Check) -> Check {
Check::Or(Box::new(self), Box::new(r))
}
pub fn constraint(self) -> Constraint {
Constraint::Check(self)
}
pub fn to_name(&self) -> String {
match self {
Check::Lower(l, r) => format!("{}_lw_{}", l.to_string(), r.to_string()),
Check::LowerEq(l, r) => format!("{}_le_{}", l.to_string(), r.to_string()),
Check::Eq(l, r) => format!("{}_eq_{}", l.to_string(), r.to_string()),
Check::GreaterEq(l, r) => format!("{}_ge_{}", l.to_string(), r.to_string()),
Check::Greater(l, r) => format!("{}_g_{}", l.to_string(), r.to_string()),
Check::Or(l, r) => format!("{}_or_{}", l.to_name(), r.to_name()),
}
}
}
impl Display for Check {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Check::Lower(l, r) => {
f.write_fmt(format_args!("{} < {}", l.to_string(), r.to_string()))
}
Check::LowerEq(l, r) => {
f.write_fmt(format_args!("{} <= {}", l.to_string(), r.to_string()))
}
Check::Eq(l, r) => {
f.write_fmt(format_args!("{} = {}", l.to_string(), r.to_string()))
}
Check::GreaterEq(l, r) => {
f.write_fmt(format_args!("{} >= {}", l.to_string(), r.to_string()))
}
Check::Greater(l, r) => {
f.write_fmt(format_args!("{} > {}", l.to_string(), r.to_string()))
}
Check::Or(l, r) => f.write_fmt(format_args!("({l}) OR ({r})")),
}
}
}
impl Check {
pub fn lower<L: Iden + 'static, R: Iden + 'static>(l: L, r: R) -> Check {
Check::Lower(Box::new(l), Box::new(r))
}
pub fn lower_eq<L: Iden + 'static, R: Iden + 'static>(l: L, r: R) -> Check {
Check::LowerEq(Box::new(l), Box::new(r))
}
pub fn eq<L: Iden + 'static, R: Iden + 'static>(l: L, r: R) -> Check {
Check::Eq(Box::new(l), Box::new(r))
}
pub fn greater_eq<L: Iden + 'static, R: Iden + 'static>(l: L, r: R) -> Check {
Check::GreaterEq(Box::new(l), Box::new(r))
}
pub fn greater<L: Iden + 'static, R: Iden + 'static>(l: L, r: R) -> Check {
Check::Greater(Box::new(l), Box::new(r))
}
}
}
pub struct CheckUSize(usize);
impl Iden for CheckUSize {
fn unquoted(&self, s: &mut dyn Write) {
s.write_str(&format!("{}", self.0)).ok();
}
}
pub trait AsIden<I: Iden> {
fn iden(self) -> I;
}
impl AsIden<CheckUSize> for usize {
fn iden(self) -> CheckUSize {
CheckUSize(self)
}
}
pub enum Constraint {
Check(Check),
}
impl Constraint {
fn to_name(&self) -> String {
match self {
Constraint::Check(check) => format!("ck_{}", check.to_name()),
}
}
}
impl From<Check> for Constraint {
fn from(value: Check) -> Self {
Self::Check(value)
}
}
impl Display for Constraint {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Constraint::Check(check) => f.write_fmt(format_args!("CHECK {check}")),
}
}
}
async fn create_constraint<T: Iden, C: Into<Constraint>>(
m: &SchemaManager<'_>,
table: T,
c: C,
) -> Result<(), DbErr> {
let c = c.into();
m.get_connection()
.execute_unprepared(&format!(
"ALTER TABLE {} ADD CONSTRAINT {} {}",
table.to_string(),
c.to_name(),
c
))
.await?;
Ok(())
}

View File

@ -3,146 +3,120 @@ use sea_orm_migration::sea_orm::Statement;
use sea_query::expr::SimpleExpr;
use crate::sea_orm::DatabaseBackend;
use crate::{auto_uuid_not_null, ts_def_now_not_null, DropTable, IntoColumnDef};
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.get_connection()
async fn up(&self, m: &SchemaManager) -> Result<(), DbErr> {
m.get_connection()
.execute(Statement::from_string(
DatabaseBackend::Postgres,
"CREATE EXTENSION \"uuid-ossp\"".to_string(),
))
.await?;
manager
.create_table(
Self::create_analytics_configs(m).await?;
Self::create_addresses(m).await?;
Ok(())
}
async fn down(&self, m: &SchemaManager) -> Result<(), DbErr> {
self.drop_table(m, Address::Addresses).await?;
self.drop_table(m, AnalyticsConfig::AnalyticsConfigs)
.await?;
Ok(())
}
}
impl Migration {
/// ```sql
/// CREATE TABLE analytics_configs
/// (
/// id uuid NOT NULL,
/// created_at timestamp with time zone DEFAULT now() NOT NULL,
/// updated_at timestamp with time zone DEFAULT now() NOT NULL,
/// deleted_at timestamp with time zone,
/// user_id uuid NOT NULL,
/// opt_out boolean DEFAULT false NOT NULL,
/// anonymize boolean DEFAULT false NOT NULL
/// );
/// ```
async fn create_analytics_configs(m: &SchemaManager<'_>) -> Result<(), DbErr> {
m.create_table(
Table::create()
// id character varying NOT NULL,
// created_at timestamp with time zone DEFAULT now() NOT NULL,
// updated_at timestamp with time zone DEFAULT now() NOT NULL,
// deleted_at timestamp with time zone,
// user_id character varying NOT NULL,
// opt_out boolean DEFAULT false NOT NULL,
// anonymize boolean DEFAULT false NOT NULL
.table(AnalyticsConfig::AnalyticsConfigs)
.col(
ColumnDef::new(AnalyticsConfig::Id)
.uuid()
.not_null()
.default(SimpleExpr::Custom("uuid_generate_v4()".into()))
.primary_key(),
)
.col(
ColumnDef::new(AnalyticsConfig::CreatedAt)
.timestamp()
.default(SimpleExpr::Custom("now()".into()))
.not_null(),
)
.col(
ColumnDef::new(AnalyticsConfig::UpdatedAt)
.timestamp()
.default(SimpleExpr::Custom("now()".into()))
.not_null(),
)
.col(ColumnDef::new(AnalyticsConfig::DeletedAt).timestamp())
.col(auto_uuid_not_null!(AnalyticsConfig::Id))
.col(ts_def_now_not_null!(AnalyticsConfig::CreatedAt))
.col(ts_def_now_not_null!(AnalyticsConfig::UpdatedAt))
.col(AnalyticsConfig::DeletedAt.col().timestamp())
.col(ColumnDef::new(AnalyticsConfig::UserId).uuid().not_null())
.col(
ColumnDef::new(AnalyticsConfig::OptOut)
AnalyticsConfig::OptOut
.col()
.boolean()
.default(false)
.not_null(),
)
.col(
ColumnDef::new(AnalyticsConfig::Anonymize)
AnalyticsConfig::Anonymize
.col()
.boolean()
.default(false)
.not_null(),
)
.to_owned(),
)
.await?;
manager
.create_table(
Table::create()
// id character varying NOT NULL,
// customer_id character varying,
// company character varying,
// first_name character varying,
// last_name character varying,
// address_1 character varying,
// address_2 character varying,
// city character varying,
// country_code character varying,
// province character varying,
// postal_code character varying,
// phone character varying,
// created_at timestamp with time zone DEFAULT now() NOT NULL,
// updated_at timestamp with time zone DEFAULT now() NOT NULL,
// deleted_at timestamp with time zone,
// metadata jsonb
.table(Address::Addresses)
.if_not_exists()
.col(
ColumnDef::new(Address::Id)
.uuid()
.not_null()
.default(SimpleExpr::Custom("uuid_generate_v4()".into()))
.primary_key(),
)
.col(ColumnDef::new(Address::CustomerId).uuid())
.col(ColumnDef::new(Address::Company).string())
.col(ColumnDef::new(Address::FirstName).string())
.col(ColumnDef::new(Address::LastName).string())
.col(ColumnDef::new(Address::Address1).string())
.col(ColumnDef::new(Address::Address2).string())
.col(ColumnDef::new(Address::City).string())
.col(ColumnDef::new(Address::CountryCode).string())
.col(ColumnDef::new(Address::Province).string())
.col(ColumnDef::new(Address::PostalCode).string())
.col(ColumnDef::new(Address::Phone).string())
.col(
ColumnDef::new(Address::CreatedAt)
.timestamp()
.default(SimpleExpr::Custom("now()".into()))
.not_null(),
)
.col(
ColumnDef::new(Address::UpdatedAt)
.timestamp()
.default(SimpleExpr::Custom("now()".into()))
.not_null(),
)
.col(ColumnDef::new(Address::DeletedAt).timestamp())
.col(ColumnDef::new(Address::Metadata).json_binary())
.to_owned(),
)
.await
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(Address::Addresses).to_owned())
.await?;
manager
.drop_table(
Table::drop()
.table(AnalyticsConfig::AnalyticsConfigs)
/// ```sql
/// CREATE TABLE addresses
/// (
/// id uuid NOT NULL,
/// customer_id uuid,
/// company character varying,
/// first_name character varying,
/// last_name character varying,
/// address_1 character varying,
/// address_2 character varying,
/// city character varying,
/// country_code character varying,
/// province character varying,
/// postal_code character varying,
/// phone character varying,
/// created_at timestamp with time zone DEFAULT now() NOT NULL,
/// updated_at timestamp with time zone DEFAULT now() NOT NULL,
/// deleted_at timestamp with time zone,
/// metadata jsonb
/// );
/// ```
async fn create_addresses(m: &SchemaManager<'_>) -> Result<(), DbErr> {
m.create_table(
Table::create()
.table(Address::Addresses)
.if_not_exists()
.col(auto_uuid_not_null!(Address::Id))
.col(Address::CustomerId.col().uuid())
.col(Address::Company.col().string())
.col(Address::FirstName.col().string())
.col(Address::LastName.col().string())
.col(Address::Address1.col().string())
.col(Address::Address2.col().string())
.col(Address::City.col().string())
.col(Address::CountryCode.col().string())
.col(Address::Province.col().string())
.col(Address::PostalCode.col().string())
.col(Address::Phone.col().string())
.col(ts_def_now_not_null!(Address::CreatedAt))
.col(ts_def_now_not_null!(Address::UpdatedAt))
.col(Address::DeletedAt.col().timestamp())
.col(Address::Metadata.col().json_binary())
.to_owned(),
)
.await?;
manager
.get_connection()
.execute(Statement::from_string(
DatabaseBackend::Postgres,
"DROP SCHEMA jobs".to_string(),
))
.await?;
Ok(())
.await
}
}

View File

@ -5,7 +5,7 @@ use sea_query::expr::SimpleExpr;
use crate::types::{
ClaimItemReason, ClaimOrderFulfillmentStatus, ClaimOrderPaymentStatus, ClaimOrderType,
};
use crate::{auto_uuid_not_null, ts_def_now_not_null};
use crate::{auto_uuid_not_null, ts_def_now_not_null, DropTable, IntoColumnDef};
/// ```sql
/// CREATE TABLE claim_images
@ -83,22 +83,12 @@ impl MigrationTrait for Migration {
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(ClaimImage::ClaimImages).to_owned())
.await?;
manager
.drop_table(Table::drop().table(ClaimItem::ClaimItems).to_owned())
.await?;
manager
.drop_table(Table::drop().table(ClaimItemTag::ClaimItemTags).to_owned())
.await?;
manager
.drop_table(Table::drop().table(ClaimOrder::ClaimOrders).to_owned())
.await?;
manager
.drop_table(Table::drop().table(ClaimTag::ClaimTags).to_owned())
.await?;
async fn down(&self, m: &SchemaManager) -> Result<(), DbErr> {
self.drop_table(m, ClaimImage::ClaimImages).await?;
self.drop_table(m, ClaimItem::ClaimItems).await?;
self.drop_table(m, ClaimItemTag::ClaimItemTags).await?;
self.drop_table(m, ClaimOrder::ClaimOrders).await?;
self.drop_table(m, ClaimTag::ClaimTags).await?;
Ok(())
}
}
@ -116,11 +106,19 @@ impl Migration {
.create_table(
Table::create()
.table(ClaimItemTag::ClaimItemTags)
.col(ColumnDef::new(ClaimItemTag::ItemId).uuid().not_null())
.col(ColumnDef::new(ClaimItemTag::TagId).uuid().not_null())
.col(ClaimItemTag::ItemId.col().uuid().not_null())
.col(ClaimItemTag::TagId.col().uuid().not_null())
.primary_key(
IndexCreateStatement::new()
.table(ClaimItemTag::ClaimItemTags)
.col(ClaimItemTag::ItemId)
.col(ClaimItemTag::TagId)
.primary(),
)
.to_owned(),
)
.await
.await?;
Ok(())
}
/// ```sql
@ -145,23 +143,24 @@ impl Migration {
Table::create()
.table(ClaimItem::ClaimItems)
.col(auto_uuid_not_null!(ClaimItem::Id))
.col(ColumnDef::new(ClaimItem::ClaimOrderId).uuid().not_null())
.col(ColumnDef::new(ClaimItem::ItemId).uuid().not_null())
.col(ColumnDef::new(ClaimItem::VariantId).uuid().not_null())
.col(ClaimItem::ClaimOrderId.col().uuid().not_null())
.col(ClaimItem::ItemId.col().uuid().not_null())
.col(ClaimItem::VariantId.col().uuid().not_null())
.col(
ColumnDef::new(ClaimItem::Reason)
ClaimItem::Reason
.col()
.enumeration(
ClaimItemReason::ClaimItemReasons,
ClaimItemReason::iter().skip(1),
)
.not_null(),
)
.col(ColumnDef::new(ClaimItem::Note).string())
.col(ColumnDef::new(ClaimItem::Quantity).integer().not_null())
.col(ClaimItem::Note.col().string())
.col(ClaimItem::Quantity.col().integer().not_null())
.col(ts_def_now_not_null!(ClaimItem::CreatedAt))
.col(ts_def_now_not_null!(ClaimItem::UpdatedAt))
.col(ColumnDef::new(ClaimItem::DeletedAt).timestamp())
.col(ColumnDef::new(ClaimItem::Metadata).json_binary())
.col(ClaimItem::DeletedAt.col().timestamp())
.col(ClaimItem::Metadata.col().json_binary())
.to_owned(),
)
.await
@ -185,12 +184,12 @@ impl Migration {
Table::create()
.table(ClaimImage::ClaimImages)
.col(auto_uuid_not_null!(ClaimImage::Id))
.col(ColumnDef::new(ClaimImage::ClaimItemId).uuid().not_null())
.col(ColumnDef::new(ClaimImage::Url).string().not_null())
.col(ClaimImage::ClaimItemId.col().uuid().not_null())
.col(ClaimImage::Url.col().string().not_null())
.col(ts_def_now_not_null!(ClaimImage::CreatedAt))
.col(ts_def_now_not_null!(ClaimImage::UpdatedAt))
.col(ColumnDef::new(ClaimImage::DeletedAt).string())
.col(ColumnDef::new(ClaimImage::Metadata).json_binary())
.col(ClaimImage::DeletedAt.col().string())
.col(ClaimImage::Metadata.col().json_binary())
.to_owned(),
)
.await
@ -222,7 +221,8 @@ impl Migration {
.table(ClaimOrder::ClaimOrders)
.col(auto_uuid_not_null!(ClaimOrder::Id))
.col(
ColumnDef::new(ClaimOrder::PaymentStatus)
ClaimOrder::PaymentStatus
.col()
.enumeration(
ClaimOrderPaymentStatus::ClaimOrderPaymentStatuses,
ClaimOrderPaymentStatus::iter().skip(1),
@ -231,7 +231,8 @@ impl Migration {
.not_null(),
)
.col(
ColumnDef::new(ClaimOrder::FulfillmentStatus)
ClaimOrder::FulfillmentStatus
.col()
.enumeration(
ClaimOrderFulfillmentStatus::ClaimOrderFulfillmentStatuses,
ClaimOrderFulfillmentStatus::iter().skip(1),
@ -240,23 +241,24 @@ impl Migration {
.not_null(),
)
.col(
ColumnDef::new(ClaimOrder::ClaimOrderType)
ClaimOrder::ClaimOrderType
.col()
.enumeration(
ClaimOrderType::ClaimOrderTypes,
ClaimOrderType::iter().skip(1),
)
.not_null(),
)
.col(ColumnDef::new(ClaimOrder::OrderId).uuid().not_null())
.col(ColumnDef::new(ClaimOrder::ShippingAddressId).uuid())
.col(ColumnDef::new(ClaimOrder::RefundAmount).integer())
.col(ColumnDef::new(ClaimOrder::CanceledAt).timestamp())
.col(ClaimOrder::OrderId.col().uuid().not_null())
.col(ClaimOrder::ShippingAddressId.col().uuid())
.col(ClaimOrder::RefundAmount.col().integer())
.col(ClaimOrder::CanceledAt.col().timestamp())
.col(ts_def_now_not_null!(ClaimOrder::CreatedAt))
.col(ts_def_now_not_null!(ClaimOrder::UpdatedAt))
.col(ColumnDef::new(ClaimOrder::DeletedAt).timestamp())
.col(ColumnDef::new(ClaimOrder::Metadata).json_binary())
.col(ColumnDef::new(ClaimOrder::IdempotencyKey).uuid())
.col(ColumnDef::new(ClaimOrder::NoNotification).boolean())
.col(ClaimOrder::DeletedAt.col().timestamp())
.col(ClaimOrder::Metadata.col().json_binary())
.col(ClaimOrder::IdempotencyKey.col().uuid())
.col(ClaimOrder::NoNotification.col().boolean())
.to_owned(),
)
.await
@ -279,11 +281,11 @@ impl Migration {
Table::create()
.table(ClaimTag::ClaimTags)
.col(auto_uuid_not_null!(ClaimTag::Id))
.col(ColumnDef::new(ClaimTag::Value).string())
.col(ClaimTag::Value.col().string())
.col(ts_def_now_not_null!(ClaimTag::CreatedAt))
.col(ts_def_now_not_null!(ClaimTag::UpdatedAt))
.col(ColumnDef::new(ClaimTag::DeletedAt).timestamp())
.col(ColumnDef::new(ClaimTag::Metadata).json_binary())
.col(ClaimTag::DeletedAt.col().timestamp())
.col(ClaimTag::Metadata.col().json_binary())
.to_owned(),
)
.await

View File

@ -1,7 +1,7 @@
use sea_orm_migration::prelude::*;
use sea_query::expr::SimpleExpr;
use crate::{auto_uuid_not_null, ts_def_now_not_null};
use crate::{auto_uuid_not_null, ts_def_now_not_null, DropTable, IntoColumnDef};
#[derive(DeriveMigrationName)]
pub struct Migration;
@ -16,12 +16,9 @@ impl MigrationTrait for Migration {
}
async fn down(&self, m: &SchemaManager) -> Result<(), DbErr> {
m.drop_table(Table::drop().table(Country::Countries).to_owned())
.await?;
m.drop_table(Table::drop().table(Currency::Currencies).to_owned())
.await?;
m.drop_table(Table::drop().table(Currency::Currencies).to_owned())
.await?;
self.drop_table(m, Country::Countries).await?;
self.drop_table(m, Currency::Currencies).await?;
self.drop_table(m, Currency::Currencies).await?;
Ok(())
}
}
@ -44,12 +41,12 @@ impl Migration {
Table::create()
.table(Country::Countries)
.col(auto_uuid_not_null!(Country::Id))
.col(ColumnDef::new(Country::Iso2).char_len(2).not_null())
.col(ColumnDef::new(Country::Iso3).char_len(3).not_null())
.col(ColumnDef::new(Country::NumCode).integer().not_null())
.col(ColumnDef::new(Country::Name).string().not_null())
.col(ColumnDef::new(Country::DisplayName).string().not_null())
.col(ColumnDef::new(Country::RegionId).uuid())
.col(Country::Iso2.col().char_len(2).not_null())
.col(Country::Iso3.col().char_len(3).not_null())
.col(Country::NumCode.col().integer().not_null())
.col(Country::Name.col().string().not_null())
.col(Country::DisplayName.col().string().not_null())
.col(Country::RegionId.col().uuid())
.to_owned(),
)
.await?;
@ -83,10 +80,10 @@ impl Migration {
m.create_table(
Table::create()
.table(Currency::Currencies)
.col(ColumnDef::new(Currency::Code).string().not_null())
.col(ColumnDef::new(Currency::Symbol).string().not_null())
.col(ColumnDef::new(Currency::SymbolNative).string().not_null())
.col(ColumnDef::new(Currency::Name).string().not_null())
.col(Currency::Code.col().string().not_null())
.col(Currency::Symbol.col().string().not_null())
.col(Currency::SymbolNative.col().string().not_null())
.col(Currency::Name.col().string().not_null())
.to_owned(),
)
.await?;
@ -94,6 +91,7 @@ impl Migration {
IndexCreateStatement::new()
.table(Currency::Currencies)
.col(Currency::Code)
.primary()
.to_owned(),
)
.await?;
@ -120,27 +118,29 @@ impl Migration {
Table::create()
.table(Region::Regions)
.col(auto_uuid_not_null!(Region::Id))
.col(ColumnDef::new(Region::Name).string().not_null())
.col(ColumnDef::new(Region::CurrencyCode).string().not_null())
.col(ColumnDef::new(Region::TaxRate).double().not_null())
.col(ColumnDef::new(Region::TaxCode).string().not_null())
.col(Region::Name.col().string().not_null())
.col(Region::CurrencyCode.col().string().not_null())
.col(Region::TaxRate.col().double().not_null())
.col(Region::TaxCode.col().string().not_null())
.col(ts_def_now_not_null!(Region::CreatedAt))
.col(ts_def_now_not_null!(Region::UpdatedAt))
.col(ColumnDef::new(Region::DeletedAt).timestamp())
.col(ColumnDef::new(Region::Metadata).json_binary())
.col(Region::DeletedAt.col().timestamp())
.col(Region::Metadata.col().json_binary())
.col(
ColumnDef::new(Region::GiftCardsTaxable)
Region::GiftCardsTaxable
.col()
.boolean()
.default(true)
.not_null(),
)
.col(
ColumnDef::new(Region::AutomaticTaxes)
Region::AutomaticTaxes
.col()
.boolean()
.default(true)
.not_null(),
)
.col(ColumnDef::new(Region::TaxProviderId).uuid().not_null())
.col(Region::TaxProviderId.col().uuid().not_null())
.to_owned(),
)
.await?;

View File

@ -0,0 +1,338 @@
use std::fmt::{Display, Formatter};
use async_trait::async_trait;
use sea_orm_migration::prelude::*;
use crate::constraint::Check;
use crate::DbErr;
#[macro_export]
macro_rules! ts_def_now_not_null {
($identifier: expr) => {
ColumnDef::new($identifier)
.timestamp()
.default(SimpleExpr::Custom("now()".into()))
.not_null()
};
}
#[macro_export]
macro_rules! auto_uuid_not_null {
($identifier: expr) => {
ColumnDef::new($identifier)
.uuid()
.not_null()
.default(SimpleExpr::Custom("public.uuid_generate_v4()".into()))
.primary_key()
};
}
#[async_trait::async_trait]
pub trait DropTable {
async fn drop_table<I: Iden + 'static>(
&self,
manager: &SchemaManager<'_>,
identifier: I,
) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(identifier).if_exists().to_owned())
.await
}
}
impl<T: MigrationTrait> DropTable for T {}
pub trait IntoColumnDef: IntoIden {
fn col(self) -> ColumnDef
where
Self: Sized,
{
ColumnDef::new(self)
}
}
impl<T: IntoIden> IntoColumnDef for T {}
#[async_trait]
pub trait CreateIndexExt {
async fn create_2col_idx<T, C1, C2>(&self, table: T, col1: C1, col2: C2) -> Result<(), DbErr>
where
T: IntoIden + Copy + 'static + Sync + Send,
C1: IntoIden + Copy + 'static + Sync + Send,
C2: IntoIden + Copy + 'static + Sync + Send;
}
#[async_trait]
impl CreateIndexExt for SchemaManager<'_> {
async fn create_2col_idx<T, C1, C2>(&self, table: T, col1: C1, col2: C2) -> Result<(), DbErr>
where
T: IntoIden + Copy + 'static + Sync + Send,
C1: IntoIden + Copy + 'static + Sync + Send,
C2: IntoIden + Copy + 'static + Sync + Send,
{
self.create_index(
IndexCreateStatement::new()
.table(table)
.col(col1)
.col(col2)
.name(&to_pk2_name(table, col1, col2))
.primary()
.if_not_exists()
.to_owned(),
)
.await
}
}
pub fn to_pk2_name<T1: IntoIden, C1: IntoIden, C2: IntoIden>(t1: T1, c1: C1, c2: C2) -> String {
let t1 = to_snake(t1.into_iden().to_string());
let c1 = to_snake(c1.into_iden().to_string());
let c2 = to_snake(c2.into_iden().to_string());
format!("pk_{}_{}_{}", t1, c1, c2)
}
pub fn to_fk_name<T1: IntoIden, C1: IntoIden, T2: IntoIden, C2: IntoIden>(
t1: T1,
c1: C1,
t2: T2,
c2: C2,
) -> String {
let t1 = to_snake(t1.into_iden().to_string());
let c1 = to_snake(c1.into_iden().to_string());
let t2 = to_snake(t2.into_iden().to_string());
let c2 = to_snake(c2.into_iden().to_string());
format!("fk_{}_{}_{}_{}", t1, c1, t2, c2)
}
fn to_snake(s: String) -> String {
s.chars().fold(String::new(), |mut m, c| {
if c.is_ascii_uppercase() {
m.push('_');
}
m.push(c.to_ascii_lowercase());
m
})
}
pub mod constraint {
use std::fmt::{Display, Formatter};
use crate::{Constraint, Iden};
pub enum Check {
Lower(Box<dyn Iden>, Box<dyn Iden>),
LowerEq(Box<dyn Iden>, Box<dyn Iden>),
Eq(Box<dyn Iden>, Box<dyn Iden>),
GreaterEq(Box<dyn Iden>, Box<dyn Iden>),
Greater(Box<dyn Iden>, Box<dyn Iden>),
NotNull(Box<dyn Iden>),
Or(Box<Check>, Box<Check>),
}
impl std::ops::BitOr for Check {
type Output = Check;
fn bitor(self, rhs: Self) -> Self::Output {
self.or(rhs)
}
}
impl Check {
pub fn or(self, r: Check) -> Check {
Check::Or(Box::new(self), Box::new(r))
}
pub fn constraint(self) -> Constraint {
Constraint::Check(self)
}
pub fn to_name(&self) -> String {
match self {
Check::Lower(l, r) => format!("{}_lw_{}", l.to_string(), r.to_string()),
Check::LowerEq(l, r) => format!("{}_le_{}", l.to_string(), r.to_string()),
Check::Eq(l, r) => format!("{}_eq_{}", l.to_string(), r.to_string()),
Check::GreaterEq(l, r) => format!("{}_ge_{}", l.to_string(), r.to_string()),
Check::Greater(l, r) => format!("{}_g_{}", l.to_string(), r.to_string()),
Check::Or(l, r) => format!("{}_or_{}", l.to_name(), r.to_name()),
Check::NotNull(l) => format!("{}_inn", l.to_string()),
}
}
}
impl Display for Check {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Check::Lower(l, r) => {
f.write_fmt(format_args!("{} < {}", l.to_string(), r.to_string()))
}
Check::LowerEq(l, r) => {
f.write_fmt(format_args!("{} <= {}", l.to_string(), r.to_string()))
}
Check::Eq(l, r) => {
f.write_fmt(format_args!("{} = {}", l.to_string(), r.to_string()))
}
Check::GreaterEq(l, r) => {
f.write_fmt(format_args!("{} >= {}", l.to_string(), r.to_string()))
}
Check::Greater(l, r) => {
f.write_fmt(format_args!("{} > {}", l.to_string(), r.to_string()))
}
Check::Or(l, r) => f.write_fmt(format_args!("({l}) OR ({r})")),
Check::NotNull(l) => f.write_fmt(format_args!("{} IS NOT NULL", l.to_string())),
}
}
}
impl Check {
pub fn less<L: Iden + 'static, R: Iden + 'static>(l: L, r: R) -> Check {
Check::Lower(Box::new(l), Box::new(r))
}
pub fn less_eq<L: Iden + 'static, R: Iden + 'static>(l: L, r: R) -> Check {
Check::LowerEq(Box::new(l), Box::new(r))
}
pub fn eq<L: Iden + 'static, R: Iden + 'static>(l: L, r: R) -> Check {
Check::Eq(Box::new(l), Box::new(r))
}
pub fn greater_eq<L: Iden + 'static, R: Iden + 'static>(l: L, r: R) -> Check {
Check::GreaterEq(Box::new(l), Box::new(r))
}
pub fn greater<L: Iden + 'static, R: Iden + 'static>(l: L, r: R) -> Check {
Check::Greater(Box::new(l), Box::new(r))
}
pub fn not_null<L: Iden + 'static>(l: L) -> Check {
Check::NotNull(Box::new(l))
}
}
}
pub struct CheckUSize(usize);
impl Iden for CheckUSize {
fn unquoted(&self, s: &mut dyn Write) {
s.write_str(&format!("{}", self.0)).ok();
}
}
pub trait AsIden<I: Iden> {
fn iden(self) -> I;
}
impl AsIden<CheckUSize> for usize {
fn iden(self) -> CheckUSize {
CheckUSize(self)
}
}
pub enum Constraint {
Check(Check),
}
impl Constraint {
fn to_name(&self) -> String {
match self {
Constraint::Check(check) => format!("ck_{}", check.to_name()),
}
}
}
impl From<Check> for Constraint {
fn from(value: Check) -> Self {
Self::Check(value)
}
}
impl Display for Constraint {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Constraint::Check(check) => f.write_fmt(format_args!("CHECK {check}")),
}
}
}
#[async_trait(?Send)]
pub trait CreateConstraint {
async fn create_constraint<T: Iden, C: Into<Constraint>>(
&self,
table: T,
c: C,
) -> Result<(), DbErr>;
}
#[async_trait(?Send)]
impl CreateConstraint for SchemaManager<'_> {
async fn create_constraint<T: Iden, C>(&self, table: T, c: C) -> Result<(), DbErr>
where
C: Into<Constraint>,
{
let c = c.into();
self.get_connection()
.execute_unprepared(&format!(
"ALTER TABLE {} ADD CONSTRAINT {} {}",
table.to_string(),
c.to_name(),
c
))
.await?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use insta::assert_ron_snapshot;
use sea_orm_migration::prelude::*;
use crate::constraint::Check;
#[derive(Iden)]
pub enum ShippingMethod {
ShippingMethods,
Id,
ShippingOptionId,
OrderId,
CartId,
SwapId,
ReturnId,
Price,
Data,
ClaimOrderId,
}
fn complex_or_check() -> Check {
Check::not_null(ShippingMethod::ClaimOrderId)
| Check::not_null(ShippingMethod::OrderId)
| Check::not_null(ShippingMethod::CartId)
| Check::not_null(ShippingMethod::SwapId)
| Check::not_null(ShippingMethod::ReturnId)
}
#[test]
fn combine_not_null_or_sql() {
let s = complex_or_check();
assert_ron_snapshot!(s.to_string());
}
#[test]
fn combine_not_null_or_name() {
let s = complex_or_check();
assert_ron_snapshot!(s.to_name());
}
#[test]
fn constraint_combine_not_null_or_sql() {
let c = complex_or_check().constraint();
assert_ron_snapshot!(c.to_string());
}
#[test]
fn constraint_combine_not_null_or_name() {
let c = complex_or_check().constraint();
assert_ron_snapshot!(c.to_name());
}
}

View File

@ -0,0 +1,5 @@
---
source: migration/src/sea_ext/mod.rs
expression: s.to_name()
---
"claim_order_id_inn_or_order_id_inn_or_cart_id_inn_or_swap_id_inn_or_return_id_inn"

View File

@ -0,0 +1,5 @@
---
source: migration/src/sea_ext/mod.rs
expression: s.to_string()
---
"((((claim_order_id IS NOT NULL) OR (order_id IS NOT NULL)) OR (cart_id IS NOT NULL)) OR (swap_id IS NOT NULL)) OR (return_id IS NOT NULL)"

View File

@ -0,0 +1,5 @@
---
source: migration/src/sea_ext/mod.rs
expression: c.to_name()
---
"ck_claim_order_id_inn_or_order_id_inn_or_cart_id_inn_or_swap_id_inn_or_return_id_inn"

View File

@ -0,0 +1,5 @@
---
source: migration/src/sea_ext/mod.rs
expression: c.to_string()
---
"CHECK ((((claim_order_id IS NOT NULL) OR (order_id IS NOT NULL)) OR (cart_id IS NOT NULL)) OR (swap_id IS NOT NULL)) OR (return_id IS NOT NULL)"

View File

@ -1,8 +1,10 @@
use sea_orm_migration::prelude::*;
use crate::constraint::Check;
use crate::sea_orm::Iterable;
use crate::{
auto_uuid_not_null, create_type, drop_type, ts_def_now_not_null, DropTable, IntoColumnDef,
auto_uuid_not_null, create_type, drop_type, ts_def_now_not_null, AsIden, CreateConstraint,
DropTable, IntoColumnDef,
};
#[derive(DeriveMigrationName)]
@ -114,12 +116,33 @@ impl Migration {
/// data jsonb NOT NULL,
/// claim_order_id uuid,
/// CONSTRAINT "CHK_64c6812fe7815be30d688df513" CHECK ((price >= 0)),
/// CONSTRAINT "CHK_a7020b08665bbd64d84a6641cf" CHECK (((claim_order_id
/// IS NOT NULL) OR (order_id IS NOT NULL) OR
/// (cart_id IS NOT NULL) OR (swap_id IS NOT NULL) OR
/// (return_id IS NOT NULL))) );
/// CONSTRAINT "CHK_a7020b08665bbd64d84a6641cf" CHECK (
/// (
/// (claim_order_id IS NOT NULL)
/// OR (order_id IS NOT NULL)
/// OR (cart_id IS NOT NULL)
/// OR (swap_id IS NOT NULL)
/// OR (return_id IS NOT NULL)
/// )
/// )
/// );
/// ```
async fn create_shipping_methods(m: &SchemaManager<'_>) -> Result<(), DbErr> {
m.create_constraint(
ShippingMethod::ShippingMethods,
Check::greater_eq(ShippingMethod::Price, 0.iden()),
)
.await?;
m.create_constraint(
ShippingMethod::ShippingMethods,
Check::not_null(ShippingMethod::ClaimOrderId)
| Check::not_null(ShippingMethod::OrderId)
| Check::not_null(ShippingMethod::CartId)
| Check::not_null(ShippingMethod::SwapId)
| Check::not_null(ShippingMethod::ReturnId),
)
.await?;
Ok(())
}
@ -161,6 +184,11 @@ impl Migration {
/// );
/// ````
async fn create_shipping_options(m: &SchemaManager<'_>) -> Result<(), DbErr> {
m.create_constraint(
ShippingOption::ShippingOptions,
Check::greater_eq(ShippingOption::Amount, 0.iden()),
)
.await?;
Ok(())
}