Skip to main content

rusty_cdk_core/appconfig/
builder.rs

1use std::marker::PhantomData;
2
3use serde_json::Value;
4use crate::appconfig::{Application, ApplicationProperties, ApplicationRef, ApplicationType, ConfigurationProfile, ConfigurationProfileProperties, ConfigurationProfileRef, ConfigurationProfileType, DeploymentStrategy, DeploymentStrategyProperties, DeploymentStrategyRef, DeploymentStrategyType, Environment, EnvironmentProperties, EnvironmentRef, EnvironmentType, Validator};
5use crate::iam::{RoleRef};
6use crate::shared::Id;
7use crate::stack::{Resource, StackBuilder};
8use crate::type_state;
9use crate::wrappers::{AppConfigName, DeploymentDurationInMinutes, GrowthFactor, LocationUri};
10
11/// Builder for AWS AppConfig applications.
12///
13/// # Example
14///
15/// ```rust
16/// use rusty_cdk_core::stack::StackBuilder;
17/// use rusty_cdk_core::appconfig::ApplicationBuilder;
18/// use rusty_cdk_core::wrappers::*;
19/// use rusty_cdk_macros::app_config_name;
20///
21/// let mut stack_builder = StackBuilder::new();
22///
23/// let app = ApplicationBuilder::new("my-app", app_config_name!("MyApplication"))
24///     .build(&mut stack_builder);
25/// ```
26pub struct ApplicationBuilder {
27    id: Id,
28    name: String,
29}
30
31impl ApplicationBuilder {
32    /// Creates a new AppConfig application builder.
33    ///
34    /// # Arguments
35    /// * `id` - Unique identifier for the application
36    /// * `name` - Name of the AppConfig application
37    pub fn new(id: &str, name: AppConfigName) -> Self {
38        Self {
39            id: Id(id.to_string()),
40            name: name.0,
41        }
42    }
43
44    pub fn build(self, stack_builder: &mut StackBuilder) -> ApplicationRef {
45        let resource_id = Resource::generate_id("AppConfigApp");
46
47        stack_builder.add_resource(Application {
48            id: self.id,
49            resource_id: resource_id.clone(),
50            r#type: ApplicationType::ApplicationType,
51            properties: ApplicationProperties { name: self.name },
52        });
53
54        ApplicationRef::internal_new(resource_id)
55    }
56}
57
58// pub enum LocationUri {
59//     Hosted,
60//     CodePipeline(String), // codepipeline://<pipeline name>.
61//     SecretsManager(String), // secretsmanager://<secret name>
62//     S3(String) // s3://<bucket>/<objectKey>
63//     // SSM, AWS Systems Manager Parameter Store
64// }
65// 
66// impl From<LocationUri> for String {
67//     fn from(value: LocationUri) -> Self {
68//         match value {
69//             LocationUri::Hosted => "hosted".to_string(),
70//             LocationUri::CodePipeline(l) => l.to_string(),
71//             LocationUri::SecretsManager(l) => l.to_string(),
72//             LocationUri::S3(l) => l.to_string(),
73//         }
74//     }
75// }
76
77#[derive(Debug, Clone)]
78pub enum ConfigType {
79    FeatureFlags,
80    Freeform,
81}
82
83impl From<ConfigType> for String {
84    fn from(value: ConfigType) -> Self {
85        match value {
86            ConfigType::FeatureFlags => "AWS.AppConfig.FeatureFlags".to_string(),
87            ConfigType::Freeform => "AWS.Freeform".to_string(),
88        }
89    }
90}
91
92#[derive(Debug, Clone)]
93pub enum DeletionProtectionCheck {
94    AccountDefault,
95    Apply,
96    Bypass,
97}
98
99impl From<DeletionProtectionCheck> for String {
100    fn from(value: DeletionProtectionCheck) -> Self {
101        match value {
102            DeletionProtectionCheck::AccountDefault => "ACCOUNT_DEFAULT".to_string(),
103            DeletionProtectionCheck::Apply => "APPLY".to_string(),
104            DeletionProtectionCheck::Bypass => "BYPASS".to_string(),
105        }
106    }
107}
108
109/// Builder for AWS AppConfig configuration profiles.
110///
111/// # Example
112///
113/// ```rust,no_run
114/// use rusty_cdk_core::stack::StackBuilder;
115/// use rusty_cdk_core::appconfig::{ApplicationBuilder, ConfigurationProfileBuilder};
116/// use rusty_cdk_core::wrappers::*;
117/// use rusty_cdk_macros::{app_config_name, location_uri};
118///
119/// let mut stack_builder = StackBuilder::new();
120///
121/// let app = unimplemented!("create an application");
122/// let location_uri = location_uri!("hosted");
123///
124/// let profile = ConfigurationProfileBuilder::new(
125///     "my-profile",
126///     app_config_name!("MyProfile"),
127///     &app,
128///     location_uri,
129/// )
130/// .build(&mut stack_builder);
131/// ```
132pub struct ConfigurationProfileBuilder {
133    id: Id,
134    name: String,
135    application_id: Value,
136    location_uri: String,
137    deletion_protection_check: Option<String>,
138    config_type: Option<String>,
139    validators: Option<Vec<Validator>>
140}
141
142impl ConfigurationProfileBuilder {
143    /// Creates a new AppConfig configuration profile builder.
144    ///
145    /// # Arguments
146    /// * `id` - Unique identifier for the configuration profile
147    /// * `name` - Name of the configuration profile
148    /// * `application` - Reference to the parent AppConfig application
149    /// * `location_uri` - Location where the configuration is stored
150    pub fn new(id: &str, name: AppConfigName, application: &ApplicationRef, location_uri: LocationUri) -> Self {
151        Self {
152            id: Id(id.to_string()),
153            name: name.0,
154            application_id: application.get_ref(),
155            location_uri: location_uri.0,
156            deletion_protection_check: None,
157            config_type: None,
158            validators: None,
159        }
160    }
161
162    pub fn deletion_protection_check(self, deletion_protection_check: DeletionProtectionCheck) -> Self {
163        Self {
164            deletion_protection_check: Some(deletion_protection_check.into()),
165            ..self
166        }
167    }
168
169    pub fn config_type(self, config_type: ConfigType) -> Self {
170        Self {
171            config_type: Some(config_type.into()),
172            ..self
173        }
174    }
175
176    pub fn add_validator(mut self, validator: Validator) -> Self {
177        if let Some(mut validators) = self.validators {
178            validators.push(validator);
179            self.validators = Some(validators);
180        } else {
181            self.validators = Some(vec![validator]);
182        }
183
184        self
185    }
186
187    pub fn build(self, stack_builder: &mut StackBuilder) -> ConfigurationProfileRef {
188        let resource_id = Resource::generate_id("ConfigurationProfile");
189
190        stack_builder.add_resource(ConfigurationProfile {
191            id: self.id,
192            resource_id: resource_id.clone(),
193            r#type: ConfigurationProfileType::ConfigurationProfileType,
194            properties: ConfigurationProfileProperties {
195                name: self.name,
196                application_id: self.application_id,
197                deletion_protection_check: self.deletion_protection_check,
198                location_uri: self.location_uri,
199                config_type: self.config_type,
200                validators: self.validators,
201            },
202        });
203
204        ConfigurationProfileRef::internal_new(resource_id)
205    }
206}
207
208type_state!(
209    ValidatorState,
210    ValidatorStartState,
211    ValidatorChosenState,
212);
213
214/// Builder for configuration profile validators.
215pub struct ValidatorBuilder<T: ValidatorState> {
216    phantom_date: PhantomData<T>,
217    content: Option<Value>,
218    validator_type: Option<String>,
219}
220
221impl ValidatorBuilder<ValidatorStartState> {
222    pub fn new() -> Self {
223        Self {
224            phantom_date: Default::default(),
225            content: None,
226            validator_type: None,
227        }
228    }
229}
230
231impl<T: ValidatorState> ValidatorBuilder<T> {
232    pub fn lambda(self, role: &RoleRef) -> ValidatorBuilder<ValidatorChosenState> {
233        ValidatorBuilder {
234            phantom_date: Default::default(),
235            content: Some(role.get_arn()),
236            validator_type: Some("LAMBDA".to_string()),
237        }
238    }
239    
240    pub fn json_schema<S: Into<String>>(self, content: S) -> ValidatorBuilder<ValidatorChosenState> {
241        ValidatorBuilder {
242            phantom_date: Default::default(),
243            content: Some(Value::String(content.into())),
244            validator_type: Some("JSON_SCHEMA".to_string()),
245        }
246    }
247}
248
249impl ValidatorBuilder<ValidatorChosenState> {
250    pub fn build(self) -> Validator {
251        Validator {
252            content: self.content.expect("content to be present, enforced by builder"),
253            validator_type: self.validator_type.expect("validator type to be present, enforced by builder"),
254        }
255    }
256}
257
258#[derive(Debug, Clone)]
259pub enum GrowthType {
260    Linear,
261    Exponential,
262}
263
264impl From<GrowthType> for String {
265    fn from(value: GrowthType) -> Self {
266        match value {
267            GrowthType::Linear => "LINEAR".to_string(),
268            GrowthType::Exponential => "EXPONENTIAL".to_string()
269        }
270    }
271}
272
273#[derive(Debug, Clone)]
274pub enum ReplicateTo {
275    None,
276    SsmDocument,
277}
278
279impl From<ReplicateTo> for String {
280    fn from(value: ReplicateTo) -> Self {
281        match value {
282            ReplicateTo::None => "NONE".to_string(),
283            ReplicateTo::SsmDocument => "SSM_DOCUMENT".to_string(),
284        }
285    }
286}
287
288/// Builder for AWS AppConfig deployment strategies.
289pub struct DeploymentStrategyBuilder {
290    id: Id,
291    name: String,
292    deployment_duration_in_minutes: u16,
293    growth_factor: u8,
294    growth_type: Option<String>,
295    replicate_to: String,
296}
297
298impl DeploymentStrategyBuilder {
299    /// Creates a new AppConfig deployment strategy builder.
300    ///
301    /// # Arguments
302    /// * `id` - Unique identifier for the deployment strategy
303    /// * `name` - Name of the deployment strategy
304    /// * `deployment_duration_in_minutes` - Time to deploy the configuration
305    /// * `growth_factor` - Percentage of targets to receive the deployment during each interval
306    /// * `replicate_to` - Where to replicate the configuration
307    pub fn new(id: &str, name: AppConfigName, deployment_duration_in_minutes: DeploymentDurationInMinutes, growth_factor: GrowthFactor, replicate_to: ReplicateTo) -> Self {
308        Self {
309            id: Id(id.to_string()),
310            name: name.0,
311            deployment_duration_in_minutes: deployment_duration_in_minutes.0,
312            growth_factor: growth_factor.0,
313            growth_type: None,
314            replicate_to: replicate_to.into(),
315        }
316    }
317    
318    pub fn growth_type(self, growth_type: GrowthType) -> Self {
319        Self {
320            growth_type: Some(growth_type.into()),
321            ..self
322        }
323    }
324    
325    pub fn build(self, stack_builder: &mut StackBuilder) -> DeploymentStrategyRef {
326        let resource_id = Resource::generate_id("DeploymentStrategy");
327        
328        stack_builder.add_resource(DeploymentStrategy {
329            id: self.id,
330            resource_id: resource_id.clone(),
331            r#type: DeploymentStrategyType::DeploymentStrategyType,
332            properties: DeploymentStrategyProperties {
333                name: self.name,
334                deployment_duration_in_minutes: self.deployment_duration_in_minutes,
335                growth_factor: self.growth_factor,
336                replicate_to: self.replicate_to,
337                growth_type: self.growth_type,
338            },
339        });
340        
341        DeploymentStrategyRef::internal_new(resource_id)
342    }
343}
344
345/// Builder for AWS AppConfig environments.
346pub struct EnvironmentBuilder {
347    id: Id,
348    name: String,
349    application_id: Value,
350    deletion_protection_check: Option<String>
351}
352
353impl EnvironmentBuilder {
354    /// Creates a new AppConfig environment builder.
355    ///
356    /// # Arguments
357    /// * `id` - Unique identifier for the environment
358    /// * `name` - Name of the environment
359    /// * `application` - Reference to the parent AppConfig application
360    pub fn new(id: &str, name: AppConfigName, application: &ApplicationRef) -> Self {
361        Self {
362            id: Id(id.to_string()),
363            name: name.0,
364            application_id: application.get_ref(),
365            deletion_protection_check: None,
366        }
367    }
368
369    pub fn deletion_protection_check(self, deletion_protection_check: DeletionProtectionCheck) -> Self {
370        Self {
371            deletion_protection_check: Some(deletion_protection_check.into()),
372            ..self
373        }
374    }
375    
376    pub fn build(self, stack_builder: &mut StackBuilder) -> EnvironmentRef {
377        let resource_id = Resource::generate_id("Environment");
378        
379        stack_builder.add_resource(Environment {
380            id: self.id,
381            resource_id: resource_id.clone(),
382            r#type: EnvironmentType::EnvironmentType,
383            properties: EnvironmentProperties {
384                name: self.name,
385                application_id: self.application_id,
386                deletion_protection_check: self.deletion_protection_check,
387            },
388        });
389        
390        EnvironmentRef::internal_new(resource_id)
391    }
392}