Skip to main content

rusty_cdk_core/appconfig/
builder.rs

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