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