Skip to main content

rusty_cdk_core/events/
builder.rs

1use std::marker::PhantomData;
2use serde_json::Value;
3use crate::events::{FlexibleTimeWindow, RetryPolicy, Schedule, ScheduleProperties, ScheduleRef, ScheduleType, Target};
4use crate::iam::RoleRef;
5use crate::lambda::FunctionRef;
6use crate::shared::Id;
7use crate::sns::TopicRef;
8use crate::sqs::QueueRef;
9use crate::stack::{Resource, StackBuilder};
10use crate::type_state;
11use crate::wrappers::{MaxFlexibleTimeWindow, RetryPolicyEventAge, RetryPolicyRetries, ScheduleAtExpression, ScheduleCronExpression, ScheduleName, ScheduleRateExpression};
12
13type_state!(
14    ScheduleBuilderState,
15    StartState,
16    OneTimeScheduleState,
17    RepeatedScheduleState,
18);
19
20#[derive(Debug)]
21pub enum State {
22    Enabled,
23    Disabled,
24}
25
26impl From<State> for String {
27    fn from(value: State) -> String {
28        match value {
29            State::Enabled => "ENABLED".to_string(),
30            State::Disabled => "DISABLED".to_string(),
31        }
32    }
33}
34
35pub struct ScheduleBuilder<T: ScheduleBuilderState> {
36    phantom_data: PhantomData<T>,
37    id: Id,
38    start_date: Option<String>,
39    end_date: Option<String>,
40    flexible_time_window: FlexibleTimeWindow,
41    group_name: Option<String>,
42    name: Option<String>,
43    state: Option<String>,
44    schedule_expression: Option<String>,
45    target: Target
46}
47
48impl<T: ScheduleBuilderState> ScheduleBuilder<T> {
49    pub fn name(self, name: ScheduleName) -> Self {
50        Self {
51            name: Some(name.0),
52            ..self
53        }
54    }
55    
56    pub fn group_name(self, group_name: ScheduleName) -> Self {
57        Self {
58            group_name: Some(group_name.0),
59            ..self
60        }
61    }
62    
63    pub fn state(self, state: State) -> Self {
64        Self {
65            state: Some(state.into()),
66            ..self
67        }
68    }
69    
70    fn build_internal(self, stack_builder: &mut StackBuilder) -> ScheduleRef {
71        let resource_id = Resource::generate_id("Schedule");
72        stack_builder.add_resource(Schedule {
73            id: self.id,
74            resource_id: resource_id.clone(),
75            r#type: ScheduleType::ScheduleType,
76            properties: ScheduleProperties {
77                start_date: self.start_date,
78                end_date: self.end_date,
79                flexible_time_window: self.flexible_time_window,
80                group_name: self.group_name,
81                name: self.name,
82                state: self.state,
83                schedule_expression: self.schedule_expression.expect("schedule expression to be present, enforced by builder"),
84                target: self.target,
85            },
86        });
87        
88        ScheduleRef::internal_new(resource_id)
89    }
90}
91
92impl ScheduleBuilder<StartState> {
93    pub fn new(id: &str, target: Target, flexible_time_window: FlexibleTimeWindow) -> ScheduleBuilder<StartState> {
94        ScheduleBuilder {
95            phantom_data: Default::default(),
96            id: Id(id.to_string()),
97            flexible_time_window,
98            target,
99            start_date: None,
100            end_date: None,
101            group_name: None,
102            name: None,
103            state: None,
104            schedule_expression: None,
105        }
106    }
107    
108    pub fn one_time_schedule(self, expression: ScheduleAtExpression) -> ScheduleBuilder<OneTimeScheduleState> {
109        let one_time_schedule = format!("at({})", expression.0);
110        ScheduleBuilder {
111            phantom_data: Default::default(),
112            schedule_expression: Some(one_time_schedule),
113            id: self.id,
114            flexible_time_window: self.flexible_time_window,
115            group_name: self.group_name,
116            name: self.name,
117            state: self.state,
118            target: self.target,
119            start_date: None,
120            end_date: None,
121        }
122    }
123    
124    pub fn rate_schedule(self, expression: ScheduleRateExpression) -> ScheduleBuilder<RepeatedScheduleState> {
125        let rate = format!("rate({} {})", expression.0, expression.1);
126        ScheduleBuilder {
127            phantom_data: Default::default(),
128            schedule_expression: Some(rate),
129            id: self.id,
130            flexible_time_window: self.flexible_time_window,
131            group_name: self.group_name,
132            name: self.name,
133            state: self.state,
134            target: self.target,
135            start_date: self.start_date,
136            end_date: self.end_date,
137        }
138    }
139    
140    pub fn cron_schedule(self, expression: ScheduleCronExpression) -> ScheduleBuilder<RepeatedScheduleState> {
141        let schedule = format!("cron({})", expression.0);
142        ScheduleBuilder {
143            phantom_data: Default::default(),
144            schedule_expression: Some(schedule),
145            id: self.id,
146            flexible_time_window: self.flexible_time_window,
147            group_name: self.group_name,
148            name: self.name,
149            state: self.state,
150            target: self.target,
151            start_date: self.start_date,
152            end_date: self.end_date,
153        }
154    }
155}
156
157impl ScheduleBuilder<OneTimeScheduleState> {
158    pub fn build(self, stack_builder: &mut StackBuilder) -> ScheduleRef {
159        self.build_internal(stack_builder)
160    }
161}
162
163impl ScheduleBuilder<RepeatedScheduleState> {
164    // TODO better validation
165    pub fn start_date(self, start_date: String) -> Self {
166        Self {
167            start_date: Some(start_date),
168            ..self
169        }
170    }
171
172    // TODO better validation
173    pub fn end_date(self, end_date: String) -> Self {
174        Self {
175            end_date: Some(end_date),
176            ..self
177        }
178    }
179    
180    pub fn build(self, stack_builder: &mut StackBuilder) -> ScheduleRef {
181        self.build_internal(stack_builder)
182    }
183}
184
185type_state!(
186    TargetBuilderState,
187    TargetStartState,
188    JsonTargetState,
189    NormalTargetState,
190);
191
192pub struct TargetBuilder<T: TargetBuilderState> {
193    phantom_data: PhantomData<T>,
194    target_arn: Value,
195    role_arn: Value,
196    input: Option<String>,
197    retry_policy: Option<RetryPolicy>,
198}
199
200pub enum JsonTarget<'a> {
201    Lambda(&'a FunctionRef)
202    // AWS SF
203    // EventBridge
204}
205
206pub enum NormalTarget<'a> {
207    Sqs(&'a QueueRef),
208    Sns(&'a TopicRef),
209    Other(Value)
210}
211
212impl TargetBuilder<TargetStartState> {
213    /// Target that accepts any string input. This is all targets **except** Lambda, Step Functions, and EventBridge
214    pub fn new_normal_target(target: NormalTarget, schedule_role: &RoleRef) -> TargetBuilder<NormalTargetState> {
215        let arn = match target {
216            NormalTarget::Sqs(r) => r.get_arn(),
217            NormalTarget::Sns(r) => r.get_arn(),
218            NormalTarget::Other(r) => r,
219        };
220        TargetBuilder {
221            phantom_data: Default::default(),
222            target_arn: arn,
223            role_arn: schedule_role.get_arn(),
224            input: None,
225            retry_policy: None,
226        }
227    }
228
229    /// Target that requires the input to be valid JSON
230    /// - Lambda
231    /// - Step Functions
232    /// - EventBridge
233    pub fn new_json_target(target: JsonTarget, schedule_role: &RoleRef) -> TargetBuilder<JsonTargetState> {
234        let target_arn = match target {
235            JsonTarget::Lambda(l) => l.get_arn(),
236        };
237        TargetBuilder {
238            phantom_data: Default::default(),
239            target_arn,
240            role_arn: schedule_role.get_arn(),
241            input: None,
242            retry_policy: None,
243        }
244    }
245}
246
247impl<T: TargetBuilderState> TargetBuilder<T> {
248    pub fn retry_policy(self, retry_policy: RetryPolicy) -> TargetBuilder<T> {
249        TargetBuilder {
250            retry_policy: Some(retry_policy),
251            phantom_data: Default::default(),
252            target_arn: self.target_arn,
253            role_arn: self.role_arn,
254            input: self.input,
255        }
256    }
257
258    pub fn build(self) -> Target {
259        Target {
260            arn: self.target_arn,
261            role_arn: self.role_arn,
262            input: self.input,
263            retry_policy: self.retry_policy,
264        }
265    }
266}
267
268impl TargetBuilder<NormalTargetState> {
269    pub fn input(self, input: String) -> Self {
270        Self {
271            input: Some(input),
272            ..self
273        }
274    }
275}
276
277impl TargetBuilder<JsonTargetState> {
278    pub fn input(self, input: Value) -> Self {
279        Self {
280            input: Some(input.to_string()),
281            ..self
282        }
283    }
284}
285
286pub enum Mode {
287    Off,
288    Flexible(MaxFlexibleTimeWindow)
289}
290
291pub struct FlexibleTimeWindowBuilder {
292    maximum_window_in_minutes: Option<u16>,
293    mode: String,
294}
295
296impl FlexibleTimeWindowBuilder {
297    pub fn new(mode: Mode) -> Self {
298        match mode {
299            Mode::Off => {
300                Self {
301                    maximum_window_in_minutes: None,
302                    mode: "OFF".to_string(),
303                }
304            }
305            Mode::Flexible(max) => {
306                Self {
307                    maximum_window_in_minutes: Some(max.0),
308                    mode: "FLEXIBLE".to_string(),
309                }
310            }
311        }
312    }
313    
314    pub fn build(self) -> FlexibleTimeWindow {
315        FlexibleTimeWindow {
316            maximum_window_in_minutes: self.maximum_window_in_minutes,
317            mode: self.mode,
318        }
319    }
320}
321
322pub struct RetryPolicyBuilder {
323    maximum_event_age_in_seconds: Option<u32>, 
324    maximum_retry_attempts: Option<u8>
325}
326
327impl RetryPolicyBuilder {
328    pub fn new() -> Self {
329        Self {
330            maximum_event_age_in_seconds: None,
331            maximum_retry_attempts: None,
332        }    
333    }
334    
335    pub fn maximum_event_age_in_seconds(self, maximum_event_age_in_seconds: RetryPolicyEventAge) -> Self {
336        Self {
337            maximum_event_age_in_seconds: Some(maximum_event_age_in_seconds.0),
338            ..self
339        }
340    }
341    
342    pub fn maximum_retry_attempts(self, maximum_retry_attempts: RetryPolicyRetries) -> Self {
343        Self {
344            maximum_retry_attempts: Some(maximum_retry_attempts.0),
345            ..self
346        }
347    }
348    
349    pub fn build(self) -> RetryPolicy {
350        RetryPolicy {
351            maximum_event_age_in_seconds: self.maximum_event_age_in_seconds,
352            maximum_retry_attempts: self.maximum_retry_attempts,
353        }
354    }
355}