cala_ledger/velocity/limit/
entity.rs1use cel_interpreter::CelExpression;
2use derive_builder::Builder;
3use serde::{Deserialize, Serialize};
4use tracing::instrument;
5
6use es_entity::*;
7
8pub use crate::param::definition::*;
9pub use cala_types::{primitives::*, velocity::*};
10
11#[derive(EsEvent, Debug, Serialize, Deserialize)]
12#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
13#[serde(tag = "type", rename_all = "snake_case")]
14#[es_event(id = "VelocityLimitId", event_context = false)]
15pub enum VelocityLimitEvent {
16 Initialized { values: VelocityLimitValues },
17}
18
19#[derive(EsEntity, Builder)]
20#[builder(pattern = "owned", build_fn(error = "EntityHydrationError"))]
21pub struct VelocityLimit {
22 pub id: VelocityLimitId,
23 values: VelocityLimitValues,
24 events: EntityEvents<VelocityLimitEvent>,
25}
26
27impl VelocityLimit {
28 pub fn id(&self) -> VelocityLimitId {
29 self.values.id
30 }
31
32 pub fn into_values(self) -> VelocityLimitValues {
33 self.values
34 }
35
36 pub fn values(&self) -> &VelocityLimitValues {
37 &self.values
38 }
39}
40
41impl TryFromEvents<VelocityLimitEvent> for VelocityLimit {
42 fn try_from_events(
43 events: EntityEvents<VelocityLimitEvent>,
44 ) -> Result<Self, EntityHydrationError> {
45 let mut builder = VelocityLimitBuilder::default();
46 for event in events.iter_all() {
47 match event {
48 VelocityLimitEvent::Initialized { values } => {
49 builder = builder.id(values.id).values(values.clone());
50 }
51 }
52 }
53 builder.events(events).build()
54 }
55}
56
57#[derive(Builder, Debug)]
59#[builder(build_fn(validate = "Self::validate"))]
60pub struct NewVelocityLimit {
61 #[builder(setter(into))]
62 pub(super) id: VelocityLimitId,
63 #[builder(setter(into))]
64 pub(super) name: String,
65 #[builder(setter(into))]
66 description: String,
67 window: Vec<NewPartitionKey>,
68 #[builder(setter(strip_option, into), default)]
69 condition: Option<String>,
70 #[builder(setter(strip_option, into), default)]
71 currency: Option<Currency>,
72 #[builder(setter(strip_option), default)]
73 params: Option<Vec<NewParamDefinition>>,
74 limit: NewLimit,
75}
76
77impl NewVelocityLimit {
78 pub fn builder() -> NewVelocityLimitBuilder {
79 NewVelocityLimitBuilder::default()
80 }
81}
82
83impl IntoEvents<VelocityLimitEvent> for NewVelocityLimit {
84 fn into_events(self) -> EntityEvents<VelocityLimitEvent> {
85 let limit = self.limit;
86 EntityEvents::init(
87 self.id,
88 [VelocityLimitEvent::Initialized {
89 values: VelocityLimitValues {
90 id: self.id,
91 name: self.name,
92 description: self.description,
93 currency: self.currency,
94 window: self
95 .window
96 .into_iter()
97 .map(|input| PartitionKey {
98 alias: input.alias,
99 value: CelExpression::try_from(input.value).expect("already validated"),
100 })
101 .collect(),
102 condition: self
103 .condition
104 .map(|expr| CelExpression::try_from(expr).expect("already validated")),
105 params: self
106 .params
107 .map(|params| params.into_iter().map(ParamDefinition::from).collect()),
108 limit: Limit {
109 timestamp_source: limit
110 .timestamp_source
111 .map(CelExpression::try_from)
112 .transpose()
113 .expect("already validated"),
114 balance: limit
115 .balance
116 .into_iter()
117 .map(|input| BalanceLimit {
118 limit_type: input.limit_type,
119 layer: CelExpression::try_from(input.layer)
120 .expect("already validated"),
121 amount: CelExpression::try_from(input.amount)
122 .expect("already validated"),
123 enforcement_direction: CelExpression::try_from(
124 input.enforcement_direction,
125 )
126 .expect("already validated"),
127 start: input.start.map(|expr| {
128 CelExpression::try_from(expr).expect("already validated")
129 }),
130 end: input.end.map(|expr| {
131 CelExpression::try_from(expr).expect("already validated")
132 }),
133 })
134 .collect(),
135 },
136 },
137 }],
138 )
139 }
140}
141
142impl NewVelocityLimitBuilder {
143 fn validate(&self) -> Result<(), String> {
144 validate_optional_expression(&self.condition)?;
145 Ok(())
146 }
147}
148
149#[derive(Clone, Builder, Debug)]
150#[builder(build_fn(validate = "Self::validate"))]
151pub struct NewPartitionKey {
152 #[builder(setter(into))]
153 alias: String,
154 #[builder(setter(into))]
155 value: String,
156}
157impl NewPartitionKey {
158 pub fn builder() -> NewPartitionKeyBuilder {
159 NewPartitionKeyBuilder::default()
160 }
161}
162impl NewPartitionKeyBuilder {
163 fn validate(&self) -> Result<(), String> {
164 validate_expression(
165 self.value
166 .as_ref()
167 .expect("Mandatory field 'value' not set"),
168 )?;
169 Ok(())
170 }
171}
172
173#[derive(Clone, Builder, Debug)]
174#[builder(build_fn(validate = "Self::validate"))]
175pub struct NewLimit {
176 #[builder(setter(strip_option, into), default)]
177 timestamp_source: Option<String>,
178 balance: Vec<NewBalanceLimit>,
179}
180impl NewLimit {
181 pub fn builder() -> NewLimitBuilder {
182 NewLimitBuilder::default()
183 }
184}
185impl NewLimitBuilder {
186 fn validate(&self) -> Result<(), String> {
187 validate_optional_expression(&self.timestamp_source)
188 }
189}
190
191#[derive(Clone, Builder, Debug)]
192#[builder(build_fn(validate = "Self::validate"))]
193pub struct NewBalanceLimit {
194 #[builder(setter(into), default)]
195 limit_type: BalanceLimitType,
196 #[builder(setter(into))]
197 layer: String,
198 #[builder(setter(into))]
199 amount: String,
200 #[builder(setter(into))]
201 enforcement_direction: String,
202 #[builder(setter(into, strip_option), default)]
203 start: Option<String>,
204 #[builder(setter(into, strip_option), default)]
205 end: Option<String>,
206}
207impl NewBalanceLimit {
208 pub fn builder() -> NewBalanceLimitBuilder {
209 NewBalanceLimitBuilder::default()
210 }
211}
212impl NewBalanceLimitBuilder {
213 fn validate(&self) -> Result<(), String> {
214 validate_expression(
215 self.layer
216 .as_ref()
217 .expect("Mandatory field 'layer' not set"),
218 )?;
219 validate_expression(
220 self.amount
221 .as_ref()
222 .expect("Mandatory field 'amount' not set"),
223 )?;
224 validate_expression(
225 self.enforcement_direction
226 .as_ref()
227 .expect("Mandatory field 'enforcement_direction' not set"),
228 )?;
229 validate_optional_expression(&self.start)?;
230 validate_optional_expression(&self.end)?;
231 Ok(())
232 }
233}
234
235#[instrument(name = "velocity_limit.validate_expression", skip(expr), fields(expression = %expr), err)]
236fn validate_expression(expr: &str) -> Result<(), String> {
237 CelExpression::try_from(expr).map_err(|e| e.to_string())?;
238 Ok(())
239}
240
241#[instrument(name = "velocity_limit.validate_optional_expression", skip(expr), err)]
242fn validate_optional_expression(expr: &Option<Option<String>>) -> Result<(), String> {
243 if let Some(Some(expr)) = expr.as_ref() {
244 CelExpression::try_from(expr.as_str()).map_err(|e| e.to_string())?;
245 }
246 Ok(())
247}