cala_server/graphql/
velocity.rs

1use async_graphql::*;
2
3use cala_ledger::VelocityControlId;
4
5use crate::app::CalaApp;
6
7use super::{
8    convert::ToGlobalId,
9    primitives::*,
10    tx_template::{ParamDefinition, ParamDefinitionInput},
11    DbOp,
12};
13
14#[derive(SimpleObject, Clone)]
15pub struct VelocityLimit {
16    id: ID,
17    velocity_limit_id: UUID,
18    name: String,
19    description: String,
20    condition: Option<Expression>,
21    window: Vec<PartitionKey>,
22    currency: Option<CurrencyCode>,
23    params: Option<Vec<ParamDefinition>>,
24    limit: Limit,
25}
26
27#[derive(SimpleObject, Clone)]
28struct Limit {
29    timestamp_source: Option<Expression>,
30    balance: Vec<BalanceLimit>,
31}
32
33#[derive(SimpleObject, Clone)]
34struct BalanceLimit {
35    layer: Expression,
36    amount: Expression,
37    normal_balance_type: Expression,
38    start: Option<Expression>,
39    end: Option<Expression>,
40}
41
42#[derive(SimpleObject, Clone)]
43struct PartitionKey {
44    alias: String,
45    value: Expression,
46}
47
48#[derive(InputObject)]
49pub(super) struct VelocityLimitCreateInput {
50    pub velocity_limit_id: UUID,
51    pub name: String,
52    pub description: String,
53    pub window: Vec<PartitionKeyInput>,
54    pub condition: Option<Expression>,
55    pub limit: LimitInput,
56    pub currency: Option<CurrencyCode>,
57    pub params: Option<Vec<ParamDefinitionInput>>,
58}
59
60#[derive(InputObject)]
61pub(super) struct PartitionKeyInput {
62    pub alias: String,
63    pub value: Expression,
64}
65
66#[derive(InputObject)]
67pub(super) struct LimitInput {
68    pub timestamp_source: Option<Expression>,
69    pub balance: Vec<BalanceLimitInput>,
70}
71
72#[derive(InputObject)]
73pub(super) struct BalanceLimitInput {
74    #[graphql(default)]
75    pub limit_type: BalanceLimitType,
76    pub layer: Expression,
77    pub amount: Expression,
78    pub normal_balance_type: Expression,
79    pub start: Option<Expression>,
80    pub end: Option<Expression>,
81}
82
83#[derive(Enum, Default, Copy, Clone, Eq, PartialEq)]
84#[graphql(remote = "cala_ledger::velocity::BalanceLimitType")]
85pub(super) enum BalanceLimitType {
86    #[default]
87    Available,
88}
89
90#[derive(SimpleObject)]
91pub(super) struct VelocityLimitCreatePayload {
92    velocity_limit: VelocityLimit,
93}
94
95#[derive(SimpleObject, Clone)]
96#[graphql(complex)]
97pub struct VelocityControl {
98    id: ID,
99    velocity_control_id: UUID,
100    name: String,
101    description: String,
102    enforcement: VelocityEnforcement,
103    condition: Option<Expression>,
104}
105
106#[ComplexObject]
107impl VelocityControl {
108    async fn limits(&self, ctx: &Context<'_>) -> Result<Vec<VelocityLimit>> {
109        let app = ctx.data_unchecked::<CalaApp>();
110        let control_id = VelocityControlId::from(self.velocity_control_id);
111
112        let res = match ctx.data_opt::<DbOp>() {
113            Some(op) => {
114                let mut op = op.try_lock().expect("Lock held concurrently");
115                app.ledger()
116                    .velocities()
117                    .list_limits_for_control_in_op(&mut op, control_id)
118                    .await?
119            }
120            None => {
121                app.ledger()
122                    .velocities()
123                    .list_limits_for_control(control_id)
124                    .await?
125            }
126        };
127
128        let limits = res.into_iter().map(VelocityLimit::from).collect();
129        Ok(limits)
130    }
131}
132
133#[derive(SimpleObject, Clone)]
134struct VelocityEnforcement {
135    velocity_enforcement_action: VelocityEnforcementAction,
136}
137
138#[derive(InputObject)]
139pub(super) struct VelocityControlCreateInput {
140    pub velocity_control_id: UUID,
141    pub name: String,
142    pub description: String,
143    pub enforcement: VelocityEnforcementInput,
144    pub condition: Option<Expression>,
145}
146
147#[derive(InputObject)]
148pub(super) struct VelocityEnforcementInput {
149    #[graphql(default)]
150    pub velocity_enforcement_action: VelocityEnforcementAction,
151}
152
153#[derive(Enum, Default, Copy, Clone, Eq, PartialEq)]
154#[graphql(remote = "cala_ledger::velocity::VelocityEnforcementAction")]
155pub(super) enum VelocityEnforcementAction {
156    #[default]
157    Reject,
158}
159
160#[derive(SimpleObject)]
161pub(super) struct VelocityControlCreatePayload {
162    velocity_control: VelocityControl,
163}
164
165#[derive(InputObject)]
166pub(super) struct VelocityControlAttachInput {
167    pub velocity_control_id: UUID,
168    pub account_id: UUID,
169    pub params: JSON,
170}
171
172#[derive(SimpleObject)]
173pub(super) struct VelocityControlAttachPayload {
174    velocity_control: VelocityControl,
175}
176
177impl From<cala_ledger::velocity::VelocityControl> for VelocityControlAttachPayload {
178    fn from(entity: cala_ledger::velocity::VelocityControl) -> Self {
179        Self {
180            velocity_control: VelocityControl::from(entity),
181        }
182    }
183}
184
185impl ToGlobalId for cala_ledger::VelocityControlId {
186    fn to_global_id(&self) -> async_graphql::types::ID {
187        async_graphql::types::ID::from(format!("velocity_control:{}", self))
188    }
189}
190
191impl From<cala_ledger::velocity::VelocityControl> for VelocityControl {
192    fn from(velocity_control: cala_ledger::velocity::VelocityControl) -> Self {
193        let cala_ledger::velocity::VelocityControlValues {
194            id,
195            name,
196            description,
197            enforcement,
198            condition,
199        } = velocity_control.into_values();
200
201        let enforcement = VelocityEnforcement::from(enforcement);
202
203        Self {
204            id: id.to_global_id(),
205            velocity_control_id: UUID::from(id),
206            name,
207            description,
208            enforcement,
209            condition: condition.map(Expression::from),
210        }
211    }
212}
213
214impl From<cala_ledger::velocity::VelocityEnforcement> for VelocityEnforcement {
215    fn from(enforcement: cala_ledger::velocity::VelocityEnforcement) -> Self {
216        Self {
217            velocity_enforcement_action: enforcement.action.into(),
218        }
219    }
220}
221
222impl From<cala_ledger::velocity::VelocityControl> for VelocityControlCreatePayload {
223    fn from(entity: cala_ledger::velocity::VelocityControl) -> Self {
224        Self {
225            velocity_control: VelocityControl::from(entity),
226        }
227    }
228}
229
230#[derive(InputObject)]
231pub(super) struct VelocityControlAddLimitInput {
232    pub velocity_control_id: UUID,
233    pub velocity_limit_id: UUID,
234}
235
236#[derive(SimpleObject)]
237pub(super) struct VelocityControlAddLimitPayload {
238    velocity_control: VelocityControl,
239}
240
241impl From<cala_ledger::velocity::VelocityControl> for VelocityControlAddLimitPayload {
242    fn from(entity: cala_ledger::velocity::VelocityControl) -> Self {
243        Self {
244            velocity_control: VelocityControl::from(entity),
245        }
246    }
247}
248
249impl ToGlobalId for cala_ledger::VelocityLimitId {
250    fn to_global_id(&self) -> async_graphql::types::ID {
251        async_graphql::types::ID::from(format!("velocity_limit:{}", self))
252    }
253}
254
255impl From<cala_ledger::velocity::VelocityLimit> for VelocityLimit {
256    fn from(velocity_limit: cala_ledger::velocity::VelocityLimit) -> Self {
257        let cala_ledger::velocity::VelocityLimitValues {
258            id,
259            name,
260            description,
261            condition,
262            currency,
263            params,
264            limit,
265            window,
266        } = velocity_limit.into_values();
267
268        let params = params.map(|params| params.into_iter().map(ParamDefinition::from).collect());
269        let window = window.into_iter().map(PartitionKey::from).collect();
270
271        Self {
272            id: id.to_global_id(),
273            velocity_limit_id: UUID::from(id),
274            name,
275            description,
276            condition: condition.map(Expression::from),
277            currency: currency.map(CurrencyCode::from),
278            params,
279            window,
280            limit: Limit::from(limit),
281        }
282    }
283}
284
285impl From<cala_ledger::velocity::Limit> for Limit {
286    fn from(limit: cala_ledger::velocity::Limit) -> Self {
287        let balance = limit.balance.into_iter().map(BalanceLimit::from).collect();
288
289        Self {
290            timestamp_source: limit.timestamp_source.map(Expression::from),
291            balance,
292        }
293    }
294}
295
296impl From<cala_ledger::velocity::BalanceLimit> for BalanceLimit {
297    fn from(balance_limit: cala_ledger::velocity::BalanceLimit) -> Self {
298        Self {
299            layer: balance_limit.layer.into(),
300            amount: balance_limit.amount.into(),
301            normal_balance_type: balance_limit.enforcement_direction.into(),
302            start: balance_limit.start.map(Expression::from),
303            end: balance_limit.end.map(Expression::from),
304        }
305    }
306}
307
308impl From<cala_ledger::velocity::PartitionKey> for PartitionKey {
309    fn from(partition_key: cala_ledger::velocity::PartitionKey) -> Self {
310        Self {
311            alias: partition_key.alias,
312            value: Expression::from(partition_key.value),
313        }
314    }
315}
316
317impl From<cala_ledger::velocity::VelocityLimit> for VelocityLimitCreatePayload {
318    fn from(entity: cala_ledger::velocity::VelocityLimit) -> Self {
319        Self {
320            velocity_limit: VelocityLimit::from(entity),
321        }
322    }
323}