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}