diff --git a/cala-ledger/.sqlx/query-c6b8a8a769c6fb0a062b2cd318d5a3d98db6cbe1b5b137686090651f12cf8b6c.json b/cala-ledger/.sqlx/query-c6b8a8a769c6fb0a062b2cd318d5a3d98db6cbe1b5b137686090651f12cf8b6c.json new file mode 100644 index 00000000..a871055d --- /dev/null +++ b/cala-ledger/.sqlx/query-c6b8a8a769c6fb0a062b2cd318d5a3d98db6cbe1b5b137686090651f12cf8b6c.json @@ -0,0 +1,46 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT v.id, e.sequence, e.event,\n v.created_at AS entity_created_at, e.recorded_at AS event_recorded_at\n FROM cala_velocity_controls v\n JOIN cala_velocity_control_events e\n ON v.data_source_id = e.data_source_id\n AND v.id = e.id\n WHERE v.data_source_id = '00000000-0000-0000-0000-000000000000'\n AND v.id = ANY($1)\n ORDER BY v.id, e.sequence", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "sequence", + "type_info": "Int4" + }, + { + "ordinal": 2, + "name": "event", + "type_info": "Jsonb" + }, + { + "ordinal": 3, + "name": "entity_created_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 4, + "name": "event_recorded_at", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "UuidArray" + ] + }, + "nullable": [ + false, + false, + false, + false, + false + ] + }, + "hash": "c6b8a8a769c6fb0a062b2cd318d5a3d98db6cbe1b5b137686090651f12cf8b6c" +} diff --git a/cala-ledger/.sqlx/query-dddb5e3a247a2172df4f26d2ba54a16b752d280d50f48c1b7156e5c77c9aefd9.json b/cala-ledger/.sqlx/query-dddb5e3a247a2172df4f26d2ba54a16b752d280d50f48c1b7156e5c77c9aefd9.json new file mode 100644 index 00000000..c2b27640 --- /dev/null +++ b/cala-ledger/.sqlx/query-dddb5e3a247a2172df4f26d2ba54a16b752d280d50f48c1b7156e5c77c9aefd9.json @@ -0,0 +1,46 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT v.id, e.sequence, e.event,\n v.created_at AS entity_created_at, e.recorded_at AS event_recorded_at\n FROM cala_velocity_limits v\n JOIN cala_velocity_limit_events e\n ON v.data_source_id = e.data_source_id\n AND v.id = e.id\n WHERE v.data_source_id = '00000000-0000-0000-0000-000000000000'\n AND v.id = ANY($1)\n ORDER BY v.id, e.sequence", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "sequence", + "type_info": "Int4" + }, + { + "ordinal": 2, + "name": "event", + "type_info": "Jsonb" + }, + { + "ordinal": 3, + "name": "entity_created_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 4, + "name": "event_recorded_at", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "UuidArray" + ] + }, + "nullable": [ + false, + false, + false, + false, + false + ] + }, + "hash": "dddb5e3a247a2172df4f26d2ba54a16b752d280d50f48c1b7156e5c77c9aefd9" +} diff --git a/cala-ledger/src/velocity/account_control/mod.rs b/cala-ledger/src/velocity/account_control/mod.rs index 3c123bb3..0f5f53b8 100644 --- a/cala-ledger/src/velocity/account_control/mod.rs +++ b/cala-ledger/src/velocity/account_control/mod.rs @@ -41,7 +41,7 @@ impl AccountControls { &self, op: &mut AtomicOperation<'_>, created_at: DateTime, - control: VelocityControlValues, + control: &VelocityControlValues, account_id: AccountId, limits: Vec, params: impl Into + std::fmt::Debug, @@ -91,8 +91,8 @@ impl AccountControls { let control = AccountVelocityControl { account_id, control_id: control.id, - condition: control.condition, - enforcement: control.enforcement, + condition: control.condition.clone(), + enforcement: control.enforcement.clone(), velocity_limits, }; diff --git a/cala-ledger/src/velocity/control/entity.rs b/cala-ledger/src/velocity/control/entity.rs index 23b05c36..1b30dbc8 100644 --- a/cala-ledger/src/velocity/control/entity.rs +++ b/cala-ledger/src/velocity/control/entity.rs @@ -37,8 +37,8 @@ impl VelocityControl { self.values } - pub fn values(&self) -> VelocityControlValues { - self.values.clone() + pub fn values(&self) -> &VelocityControlValues { + &self.values } pub fn created_at(&self) -> chrono::DateTime { diff --git a/cala-ledger/src/velocity/control/repo.rs b/cala-ledger/src/velocity/control/repo.rs index eec4e7f2..4e00663b 100644 --- a/cala-ledger/src/velocity/control/repo.rs +++ b/cala-ledger/src/velocity/control/repo.rs @@ -1,17 +1,17 @@ use sqlx::{PgPool, Postgres, Transaction}; +use std::collections::HashMap; + use super::{super::error::*, entity::*}; #[derive(Debug, Clone)] pub struct VelocityControlRepo { - _pool: PgPool, + pool: PgPool, } impl VelocityControlRepo { pub fn new(pool: &PgPool) -> Self { - Self { - _pool: pool.clone(), - } + Self { pool: pool.clone() } } pub async fn create_in_tx( @@ -62,4 +62,33 @@ impl VelocityControlRepo { Err(e) => Err(e.into()), } } + + pub async fn find_all>( + &self, + ids: &[VelocityControlId], + ) -> Result, VelocityError> { + let rows = sqlx::query_as!( + GenericEvent, + r#"SELECT v.id, e.sequence, e.event, + v.created_at AS entity_created_at, e.recorded_at AS event_recorded_at + FROM cala_velocity_controls v + JOIN cala_velocity_control_events e + ON v.data_source_id = e.data_source_id + AND v.id = e.id + WHERE v.data_source_id = '00000000-0000-0000-0000-000000000000' + AND v.id = ANY($1) + ORDER BY v.id, e.sequence"#, + ids as &[VelocityControlId] + ) + .fetch_all(&self.pool) + .await?; + let n = rows.len(); + + let ret = EntityEvents::load_n::(rows, n)? + .0 + .into_iter() + .map(|limit: VelocityControl| (limit.values().id, T::from(limit))) + .collect(); + Ok(ret) + } } diff --git a/cala-ledger/src/velocity/limit/entity.rs b/cala-ledger/src/velocity/limit/entity.rs index 78b1d336..9d5b9d9f 100644 --- a/cala-ledger/src/velocity/limit/entity.rs +++ b/cala-ledger/src/velocity/limit/entity.rs @@ -36,6 +36,10 @@ impl VelocityLimit { pub fn into_values(self) -> VelocityLimitValues { self.values } + + pub fn values(&self) -> &VelocityLimitValues { + &self.values + } } impl Entity for VelocityLimit { diff --git a/cala-ledger/src/velocity/limit/repo.rs b/cala-ledger/src/velocity/limit/repo.rs index eb51906a..21f2ee26 100644 --- a/cala-ledger/src/velocity/limit/repo.rs +++ b/cala-ledger/src/velocity/limit/repo.rs @@ -1,18 +1,19 @@ use sqlx::{PgPool, Postgres, Transaction}; -use super::{super::error::*, entity::*}; +use std::collections::HashMap; + use crate::primitives::VelocityControlId; +use super::{super::error::*, entity::*}; + #[derive(Debug, Clone)] pub struct VelocityLimitRepo { - _pool: PgPool, + pool: PgPool, } impl VelocityLimitRepo { pub fn new(pool: &PgPool) -> Self { - Self { - _pool: pool.clone(), - } + Self { pool: pool.clone() } } pub async fn create_in_tx( @@ -80,4 +81,33 @@ impl VelocityLimitRepo { let ret = EntityEvents::load_n::(rows, n)?.0; Ok(ret) } + + pub async fn find_all>( + &self, + ids: &[VelocityLimitId], + ) -> Result, VelocityError> { + let rows = sqlx::query_as!( + GenericEvent, + r#"SELECT v.id, e.sequence, e.event, + v.created_at AS entity_created_at, e.recorded_at AS event_recorded_at + FROM cala_velocity_limits v + JOIN cala_velocity_limit_events e + ON v.data_source_id = e.data_source_id + AND v.id = e.id + WHERE v.data_source_id = '00000000-0000-0000-0000-000000000000' + AND v.id = ANY($1) + ORDER BY v.id, e.sequence"#, + ids as &[VelocityLimitId] + ) + .fetch_all(&self.pool) + .await?; + let n = rows.len(); + + let ret = EntityEvents::load_n::(rows, n)? + .0 + .into_iter() + .map(|limit: VelocityLimit| (limit.values().id, T::from(limit))) + .collect(); + Ok(ret) + } } diff --git a/cala-ledger/src/velocity/mod.rs b/cala-ledger/src/velocity/mod.rs index 6c491e56..fa730c9e 100644 --- a/cala-ledger/src/velocity/mod.rs +++ b/cala-ledger/src/velocity/mod.rs @@ -8,6 +8,8 @@ mod limit; use chrono::{DateTime, Utc}; use sqlx::PgPool; +use std::collections::HashMap; + use cala_types::{entry::EntryValues, transaction::TransactionValues}; pub use crate::param::Params; @@ -181,4 +183,18 @@ impl Velocities { ) -> Result, VelocityError> { self.limits.list_for_control(op.tx(), control_id).await } + + pub async fn find_all_limits>( + &self, + limit_ids: &[VelocityLimitId], + ) -> Result, VelocityError> { + self.limits.find_all(limit_ids).await + } + + pub async fn find_all_controls>( + &self, + control_ids: &[VelocityControlId], + ) -> Result, VelocityError> { + self.controls.find_all(control_ids).await + } } diff --git a/cala-server/.sqlx/query-c6b8a8a769c6fb0a062b2cd318d5a3d98db6cbe1b5b137686090651f12cf8b6c.json b/cala-server/.sqlx/query-c6b8a8a769c6fb0a062b2cd318d5a3d98db6cbe1b5b137686090651f12cf8b6c.json new file mode 100644 index 00000000..a871055d --- /dev/null +++ b/cala-server/.sqlx/query-c6b8a8a769c6fb0a062b2cd318d5a3d98db6cbe1b5b137686090651f12cf8b6c.json @@ -0,0 +1,46 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT v.id, e.sequence, e.event,\n v.created_at AS entity_created_at, e.recorded_at AS event_recorded_at\n FROM cala_velocity_controls v\n JOIN cala_velocity_control_events e\n ON v.data_source_id = e.data_source_id\n AND v.id = e.id\n WHERE v.data_source_id = '00000000-0000-0000-0000-000000000000'\n AND v.id = ANY($1)\n ORDER BY v.id, e.sequence", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "sequence", + "type_info": "Int4" + }, + { + "ordinal": 2, + "name": "event", + "type_info": "Jsonb" + }, + { + "ordinal": 3, + "name": "entity_created_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 4, + "name": "event_recorded_at", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "UuidArray" + ] + }, + "nullable": [ + false, + false, + false, + false, + false + ] + }, + "hash": "c6b8a8a769c6fb0a062b2cd318d5a3d98db6cbe1b5b137686090651f12cf8b6c" +} diff --git a/cala-server/.sqlx/query-dddb5e3a247a2172df4f26d2ba54a16b752d280d50f48c1b7156e5c77c9aefd9.json b/cala-server/.sqlx/query-dddb5e3a247a2172df4f26d2ba54a16b752d280d50f48c1b7156e5c77c9aefd9.json new file mode 100644 index 00000000..c2b27640 --- /dev/null +++ b/cala-server/.sqlx/query-dddb5e3a247a2172df4f26d2ba54a16b752d280d50f48c1b7156e5c77c9aefd9.json @@ -0,0 +1,46 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT v.id, e.sequence, e.event,\n v.created_at AS entity_created_at, e.recorded_at AS event_recorded_at\n FROM cala_velocity_limits v\n JOIN cala_velocity_limit_events e\n ON v.data_source_id = e.data_source_id\n AND v.id = e.id\n WHERE v.data_source_id = '00000000-0000-0000-0000-000000000000'\n AND v.id = ANY($1)\n ORDER BY v.id, e.sequence", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "sequence", + "type_info": "Int4" + }, + { + "ordinal": 2, + "name": "event", + "type_info": "Jsonb" + }, + { + "ordinal": 3, + "name": "entity_created_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 4, + "name": "event_recorded_at", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "UuidArray" + ] + }, + "nullable": [ + false, + false, + false, + false, + false + ] + }, + "hash": "dddb5e3a247a2172df4f26d2ba54a16b752d280d50f48c1b7156e5c77c9aefd9" +} diff --git a/cala-server/schema.graphql b/cala-server/schema.graphql index 74c46489..12e0af39 100644 --- a/cala-server/schema.graphql +++ b/cala-server/schema.graphql @@ -411,6 +411,8 @@ type Query { transactionByExternalId(externalId: String!): Transaction txTemplate(id: UUID!): TxTemplate txTemplateByCode(code: String!): TxTemplate + velocityLimit(id: UUID!): VelocityLimit + velocityControl(id: UUID!): VelocityControl } type RangedBalance { diff --git a/cala-server/src/graphql/loader.rs b/cala-server/src/graphql/loader.rs index f0c54f09..199ef8da 100644 --- a/cala-server/src/graphql/loader.rs +++ b/cala-server/src/graphql/loader.rs @@ -3,8 +3,12 @@ use async_graphql::dataloader::Loader; use std::{collections::HashMap, sync::Arc}; use super::{ - account::Account, account_set::AccountSet, journal::Journal, transaction::Transaction, + account::Account, + account_set::AccountSet, + journal::Journal, + transaction::Transaction, tx_template::TxTemplate, + velocity::{VelocityControl, VelocityLimit}, }; use cala_ledger::{ account::{error::AccountError, *}, @@ -14,7 +18,8 @@ use cala_ledger::{ primitives::*, transaction::error::TransactionError, tx_template::error::TxTemplateError, - *, + velocity::error::VelocityError, + CalaLedger, }; pub struct LedgerDataLoader { @@ -110,3 +115,35 @@ impl Loader for LedgerDataLoader { .map_err(Arc::new) } } + +impl Loader for LedgerDataLoader { + type Value = VelocityLimit; + type Error = Arc; + + async fn load( + &self, + keys: &[VelocityLimitId], + ) -> Result, Self::Error> { + self.ledger + .velocities() + .find_all_limits(keys) + .await + .map_err(Arc::new) + } +} + +impl Loader for LedgerDataLoader { + type Value = VelocityControl; + type Error = Arc; + + async fn load( + &self, + keys: &[VelocityControlId], + ) -> Result, Self::Error> { + self.ledger + .velocities() + .find_all_controls(keys) + .await + .map_err(Arc::new) + } +} diff --git a/cala-server/src/graphql/primitives.rs b/cala-server/src/graphql/primitives.rs index b50074c8..3add1050 100644 --- a/cala-server/src/graphql/primitives.rs +++ b/cala-server/src/graphql/primitives.rs @@ -178,7 +178,7 @@ impl From for NaiveDate { } } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Clone)] #[serde(transparent)] pub struct CurrencyCode(cala_types::primitives::Currency); scalar!(CurrencyCode); diff --git a/cala-server/src/graphql/schema.rs b/cala-server/src/graphql/schema.rs index 80039cc8..be07098b 100644 --- a/cala-server/src/graphql/schema.rs +++ b/cala-server/src/graphql/schema.rs @@ -205,6 +205,24 @@ impl CoreQuery { Err(err) => Err(err.into()), } } + + async fn velocity_limit( + &self, + ctx: &Context<'_>, + id: UUID, + ) -> async_graphql::Result> { + let loader = ctx.data_unchecked::>(); + Ok(loader.load_one(VelocityLimitId::from(id)).await?) + } + + async fn velocity_control( + &self, + ctx: &Context<'_>, + id: UUID, + ) -> async_graphql::Result> { + let loader = ctx.data_unchecked::>(); + Ok(loader.load_one(VelocityControlId::from(id)).await?) + } } #[derive(Default)] diff --git a/cala-server/src/graphql/velocity.rs b/cala-server/src/graphql/velocity.rs index bbca993c..456b759c 100644 --- a/cala-server/src/graphql/velocity.rs +++ b/cala-server/src/graphql/velocity.rs @@ -11,8 +11,8 @@ use super::{ DbOp, }; -#[derive(SimpleObject)] -struct VelocityLimit { +#[derive(SimpleObject, Clone)] +pub struct VelocityLimit { id: ID, velocity_limit_id: UUID, name: String, @@ -24,13 +24,13 @@ struct VelocityLimit { limit: Limit, } -#[derive(SimpleObject)] +#[derive(SimpleObject, Clone)] struct Limit { timestamp_source: Option, balance: Vec, } -#[derive(SimpleObject)] +#[derive(SimpleObject, Clone)] struct BalanceLimit { layer: Expression, amount: Expression, @@ -39,7 +39,7 @@ struct BalanceLimit { end: Option, } -#[derive(SimpleObject)] +#[derive(SimpleObject, Clone)] struct PartitionKey { alias: String, value: Expression, @@ -92,9 +92,9 @@ pub(super) struct VelocityLimitCreatePayload { velocity_limit: VelocityLimit, } -#[derive(SimpleObject)] +#[derive(SimpleObject, Clone)] #[graphql(complex)] -struct VelocityControl { +pub struct VelocityControl { id: ID, velocity_control_id: UUID, name: String, @@ -130,7 +130,7 @@ impl VelocityControl { } } -#[derive(SimpleObject)] +#[derive(SimpleObject, Clone)] struct VelocityEnforcement { velocity_enforcement_action: VelocityEnforcementAction, }