Skip to main content

cala_ledger/velocity/limit/
entity.rs

1use 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/// Representation of a ***new*** velocity limit entity with required/optional properties and a builder.
58#[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}