From 39edaead04d637cc54ca5e76bf10790e6b5cc603 Mon Sep 17 00:00:00 2001 From: Aravinth Manivannan Date: Wed, 18 Sep 2024 16:30:14 +0530 Subject: [PATCH] feat: impl get_line_items_for_bill_id DB port --- ...94785adfb14b4b8ff0ed7c4a7079e711b8ce7.json | 94 ++++++++++++ .../db/postgres/get_line_items_for_bill_id.rs | 144 ++++++++++++++++++ .../output/db/postgres/line_item_view.rs | 26 ++-- .../adapters/output/db/postgres/mod.rs | 1 + 4 files changed, 252 insertions(+), 13 deletions(-) create mode 100644 .sqlx/query-538d43c832702b03da4a51e0b0794785adfb14b4b8ff0ed7c4a7079e711b8ce7.json create mode 100644 src/billing/adapters/output/db/postgres/get_line_items_for_bill_id.rs diff --git a/.sqlx/query-538d43c832702b03da4a51e0b0794785adfb14b4b8ff0ed7c4a7079e711b8ce7.json b/.sqlx/query-538d43c832702b03da4a51e0b0794785adfb14b4b8ff0ed7c4a7079e711b8ce7.json new file mode 100644 index 0000000..5cf8d97 --- /dev/null +++ b/.sqlx/query-538d43c832702b03da4a51e0b0794785adfb14b4b8ff0ed7c4a7079e711b8ce7.json @@ -0,0 +1,94 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT \n product_name,\n product_id,\n line_item_id,\n quantity_minor_unit,\n quantity_minor_number,\n quantity_major_unit,\n quantity_major_number,\n created_time,\n bill_id,\n price_per_unit_minor,\n price_per_unit_major,\n price_per_unit_currency,\n deleted\n FROM cqrs_billing_line_item_query\n WHERE\n bill_id = $1;", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "product_name", + "type_info": "Text" + }, + { + "ordinal": 1, + "name": "product_id", + "type_info": "Uuid" + }, + { + "ordinal": 2, + "name": "line_item_id", + "type_info": "Uuid" + }, + { + "ordinal": 3, + "name": "quantity_minor_unit", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "quantity_minor_number", + "type_info": "Int4" + }, + { + "ordinal": 5, + "name": "quantity_major_unit", + "type_info": "Text" + }, + { + "ordinal": 6, + "name": "quantity_major_number", + "type_info": "Int4" + }, + { + "ordinal": 7, + "name": "created_time", + "type_info": "Timestamptz" + }, + { + "ordinal": 8, + "name": "bill_id", + "type_info": "Uuid" + }, + { + "ordinal": 9, + "name": "price_per_unit_minor", + "type_info": "Int4" + }, + { + "ordinal": 10, + "name": "price_per_unit_major", + "type_info": "Int4" + }, + { + "ordinal": 11, + "name": "price_per_unit_currency", + "type_info": "Text" + }, + { + "ordinal": 12, + "name": "deleted", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false + ] + }, + "hash": "538d43c832702b03da4a51e0b0794785adfb14b4b8ff0ed7c4a7079e711b8ce7" +} diff --git a/src/billing/adapters/output/db/postgres/get_line_items_for_bill_id.rs b/src/billing/adapters/output/db/postgres/get_line_items_for_bill_id.rs new file mode 100644 index 0000000..cecc86b --- /dev/null +++ b/src/billing/adapters/output/db/postgres/get_line_items_for_bill_id.rs @@ -0,0 +1,144 @@ +// SPDX-FileCopyrightText: 2024 Aravinth Manivannan +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +use uuid::Uuid; + +use super::BillingDBPostgresAdapter; +use crate::billing::{ + application::port::output::db::{errors::*, get_line_items_for_bill_id::*}, + domain::line_item_aggregate::LineItem, +}; + +#[async_trait::async_trait] +impl GetLineItemsForBillIDDBPort for BillingDBPostgresAdapter { + async fn get_line_items_for_bill_id(&self, bill_id: Uuid) -> BillingDBResult> { + let mut res = sqlx::query_as!( + super::line_item_view::LineItemView, + "SELECT + product_name, + product_id, + line_item_id, + quantity_minor_unit, + quantity_minor_number, + quantity_major_unit, + quantity_major_number, + created_time, + bill_id, + price_per_unit_minor, + price_per_unit_major, + price_per_unit_currency, + deleted + FROM cqrs_billing_line_item_query + WHERE + bill_id = $1;", + bill_id + ) + .fetch_all(&self.pool) + .await?; + println!("Got len: {}", res.len()); + let mut output = Vec::with_capacity(res.len()); + res.drain(0..).for_each(|r| output.push(r.into())); + Ok(output) + } +} + +#[cfg(test)] +pub mod tests { + + use super::*; + // use crate::billing::domain::add_product_command::tests::get_customizations; + use crate::{ + billing::{ + adapters::output::db::postgres::line_item_view::LineItemView, domain::bill_aggregate::*, + }, + types::currency::*, + types::quantity::*, + utils::uuid::{tests::*, *}, + }; + + async fn create_dummy_line_item( + db: &BillingDBPostgresAdapter, + bill_id: Uuid, + line_item_id: Uuid, + ) { + let view = LineItemView::default(); + let version = 0; + sqlx::query!( + "INSERT INTO cqrs_billing_line_item_query ( + version, + product_name, + product_id, + line_item_id, + quantity_minor_unit, + quantity_minor_number, + quantity_major_unit, + quantity_major_number, + created_time, + bill_id, + price_per_unit_minor, + price_per_unit_major, + price_per_unit_currency, + deleted + ) VALUES ( + $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14 + );", + version, + view.product_name, + view.product_id, + line_item_id, + QuantityUnit::DiscreteNumber.to_string(), + view.quantity_minor_number, + QuantityUnit::DiscreteNumber.to_string(), + view.quantity_major_number, + view.created_time, + bill_id, + view.price_per_unit_minor, + view.price_per_unit_major, + Currency::INR.to_string(), + view.deleted, + ) + .execute(&db.pool) + .await + .unwrap(); + } + + #[actix_rt::test] + async fn test_postgres_get_line_items_for_bill_id() { + let settings = crate::settings::tests::get_settings().await; + settings.create_db().await; + let db = super::BillingDBPostgresAdapter::new( + sqlx::postgres::PgPool::connect(&settings.database.url) + .await + .unwrap(), + ); + + let bill_id = UUID; + + // state doesn't exist + assert!(db + .get_line_items_for_bill_id(bill_id) + .await + .unwrap() + .is_empty()); + + let u = GenerateUUID; + let li_id_1 = u.get_uuid(); + let li_id_2 = u.get_uuid(); + create_dummy_line_item(&db, bill_id, li_id_1).await; + create_dummy_line_item(&db, bill_id, li_id_2).await; + + // state exists + let res = db.get_line_items_for_bill_id(bill_id).await.unwrap(); + + assert_eq!(res.len(), 2); + assert!(res + .iter() + .any(|li| *li.bill_id() == bill_id && *li.line_item_id() == li_id_1)); + assert!(res + .iter() + .any(|li| *li.bill_id() == bill_id && *li.line_item_id() == li_id_2)); + + settings.drop_db().await; + } +} diff --git a/src/billing/adapters/output/db/postgres/line_item_view.rs b/src/billing/adapters/output/db/postgres/line_item_view.rs index 91d91bb..fd7484c 100644 --- a/src/billing/adapters/output/db/postgres/line_item_view.rs +++ b/src/billing/adapters/output/db/postgres/line_item_view.rs @@ -25,23 +25,23 @@ pub const NEW_LINE_ITEM_NON_UUID: &str = "new_line_item_non_uuid-asdfa-billing"; // be designed to reflect the response dto that will be returned to a user. #[derive(Debug, Serialize, Deserialize)] pub struct LineItemView { - product_name: String, - product_id: Uuid, - bill_id: Uuid, - created_time: OffsetDateTime, + pub product_name: String, + pub product_id: Uuid, + pub bill_id: Uuid, + pub created_time: OffsetDateTime, - line_item_id: Uuid, + pub line_item_id: Uuid, - quantity_major_number: i32, - quantity_minor_number: i32, - quantity_major_unit: String, - quantity_minor_unit: String, + pub quantity_major_number: i32, + pub quantity_minor_number: i32, + pub quantity_major_unit: String, + pub quantity_minor_unit: String, - price_per_unit_major: i32, - price_per_unit_minor: i32, - price_per_unit_currency: String, + pub price_per_unit_major: i32, + pub price_per_unit_minor: i32, + pub price_per_unit_currency: String, - deleted: bool, + pub deleted: bool, } impl Default for LineItemView { diff --git a/src/billing/adapters/output/db/postgres/mod.rs b/src/billing/adapters/output/db/postgres/mod.rs index a605999..c8c8104 100644 --- a/src/billing/adapters/output/db/postgres/mod.rs +++ b/src/billing/adapters/output/db/postgres/mod.rs @@ -10,6 +10,7 @@ use crate::db::{migrate::RunMigrations, sqlx_postgres::Postgres}; mod bill_id_exists; mod bill_view; mod errors; +mod get_line_items_for_bill_id; mod line_item_id_exists; mod line_item_view; mod next_token_id;