aws_config/default_provider/
app_name.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6use crate::provider_config::ProviderConfig;
7use aws_runtime::env_config::{EnvConfigError, EnvConfigValue};
8use aws_smithy_types::error::display::DisplayErrorContext;
9use aws_types::app_name::{AppName, InvalidAppName};
10
11/// Default App Name Provider chain
12///
13/// This provider will check the following sources in order:
14/// 1. Environment variables: `AWS_SDK_UA_APP_ID`
15/// 2. Profile files from the key `sdk_ua_app_id`
16///
17#[doc = include_str!("../profile/location_of_profile_files.md")]
18///
19/// # Examples
20///
21/// **Loads "my-app" as the app name**
22/// ```ini
23/// [default]
24/// sdk_ua_app_id = my-app
25/// ```
26///
27/// **Loads "my-app" as the app name _if and only if_ the `AWS_PROFILE` environment variable
28/// is set to `other`.**
29/// ```ini
30/// [profile other]
31/// sdk_ua_app_id = my-app
32/// ```
33pub fn default_provider() -> Builder {
34    Builder::default()
35}
36
37/// Default provider builder for [`AppName`]
38#[derive(Debug, Default)]
39pub struct Builder {
40    provider_config: ProviderConfig,
41}
42
43impl Builder {
44    /// Configure the default chain
45    ///
46    /// Exposed for overriding the environment when unit-testing providers
47    pub(crate) fn configure(self, configuration: &ProviderConfig) -> Self {
48        Self {
49            provider_config: configuration.clone(),
50        }
51    }
52
53    /// Override the profile name used by this provider
54    pub fn profile_name(mut self, name: &str) -> Self {
55        self.provider_config = self.provider_config.with_profile_name(name.to_string());
56        self
57    }
58
59    async fn fallback_app_name(&self) -> Result<Option<AppName>, EnvConfigError<InvalidAppName>> {
60        let env = self.provider_config.env();
61        let profiles = self.provider_config.profile().await;
62
63        EnvConfigValue::new()
64            .profile("sdk-ua-app-id")
65            .validate(&env, profiles, |name| AppName::new(name.to_string()))
66    }
67
68    /// Build an [`AppName`] from the default chain
69    pub async fn app_name(self) -> Option<AppName> {
70        let env = self.provider_config.env();
71        let profiles = self.provider_config.profile().await;
72
73        let standard = EnvConfigValue::new()
74            .env("AWS_SDK_UA_APP_ID")
75            .profile("sdk_ua_app_id")
76            .validate(&env, profiles, |name| AppName::new(name.to_string()));
77        let with_fallback = match standard {
78            Ok(None) => self.fallback_app_name().await,
79            other => other,
80        };
81
82        with_fallback.map_err(
83                |err| tracing::warn!(err = %DisplayErrorContext(&err), "invalid value for App Name setting"),
84            )
85            .unwrap_or(None)
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92    #[allow(deprecated)]
93    use crate::profile::profile_file::{ProfileFileKind, ProfileFiles};
94    use crate::provider_config::ProviderConfig;
95    use crate::test_case::{no_traffic_client, InstantSleep};
96    use aws_smithy_runtime_api::client::behavior_version::BehaviorVersion;
97    use aws_types::os_shim_internal::{Env, Fs};
98
99    #[tokio::test]
100    async fn prefer_env_to_profile() {
101        let fs = Fs::from_slice(&[("test_config", "[default]\nsdk-ua-app-id = wrong")]);
102        let env = Env::from_slice(&[
103            ("AWS_CONFIG_FILE", "test_config"),
104            ("AWS_SDK_UA_APP_ID", "correct"),
105        ]);
106        let app_name = Builder::default()
107            .configure(
108                &ProviderConfig::no_configuration()
109                    .with_fs(fs)
110                    .with_env(env)
111                    .with_http_client(no_traffic_client()),
112            )
113            .app_name()
114            .await;
115
116        assert_eq!(Some(AppName::new("correct").unwrap()), app_name);
117    }
118
119    // test that overriding profile_name on the root level is deprecated
120    #[tokio::test]
121    async fn profile_name_override() {
122        let fs = Fs::from_slice(&[("test_config", "[profile custom]\nsdk_ua_app_id = correct")]);
123        let conf = crate::defaults(BehaviorVersion::latest())
124            .sleep_impl(InstantSleep)
125            .fs(fs)
126            .http_client(no_traffic_client())
127            .profile_name("custom")
128            .profile_files(
129                #[allow(deprecated)]
130                ProfileFiles::builder()
131                    .with_file(
132                        #[allow(deprecated)]
133                        ProfileFileKind::Config,
134                        "test_config",
135                    )
136                    .build(),
137            )
138            .load()
139            .await;
140        assert_eq!(conf.app_name(), Some(&AppName::new("correct").unwrap()));
141    }
142
143    #[tokio::test]
144    async fn load_from_profile() {
145        let fs = Fs::from_slice(&[("test_config", "[default]\nsdk_ua_app_id = correct")]);
146        let env = Env::from_slice(&[("AWS_CONFIG_FILE", "test_config")]);
147        let app_name = Builder::default()
148            .configure(
149                &ProviderConfig::empty()
150                    .with_fs(fs)
151                    .with_env(env)
152                    .with_http_client(no_traffic_client()),
153            )
154            .app_name()
155            .await;
156
157        assert_eq!(Some(AppName::new("correct").unwrap()), app_name);
158    }
159
160    #[tokio::test]
161    async fn load_from_profile_old_name() {
162        let fs = Fs::from_slice(&[("test_config", "[default]\nsdk-ua-app-id = correct")]);
163        let env = Env::from_slice(&[("AWS_CONFIG_FILE", "test_config")]);
164        let app_name = Builder::default()
165            .configure(
166                &ProviderConfig::empty()
167                    .with_fs(fs)
168                    .with_env(env)
169                    .with_http_client(no_traffic_client()),
170            )
171            .app_name()
172            .await;
173
174        assert_eq!(Some(AppName::new("correct").unwrap()), app_name);
175    }
176}