aws_runtime/
endpoint_override.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6//! Endpoint override detection for business metrics tracking
7
8use aws_smithy_runtime_api::box_error::BoxError;
9use aws_smithy_runtime_api::client::interceptors::context::BeforeSerializationInterceptorContextRef;
10use aws_smithy_runtime_api::client::interceptors::Intercept;
11use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin;
12use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer};
13
14use crate::sdk_feature::AwsSdkFeature;
15
16/// Interceptor that detects custom endpoint URLs for business metrics
17#[derive(Debug, Default)]
18#[non_exhaustive]
19pub struct EndpointOverrideInterceptor;
20
21impl EndpointOverrideInterceptor {
22    /// Creates a new EndpointOverrideInterceptor
23    pub fn new() -> Self {
24        Self
25    }
26}
27
28impl Intercept for EndpointOverrideInterceptor {
29    fn name(&self) -> &'static str {
30        "EndpointOverrideInterceptor"
31    }
32
33    fn read_before_execution(
34        &self,
35        _context: &BeforeSerializationInterceptorContextRef<'_>,
36        cfg: &mut ConfigBag,
37    ) -> Result<(), BoxError> {
38        // Check if endpoint_url was set in config
39        if cfg
40            .load::<aws_types::endpoint_config::EndpointUrl>()
41            .is_some()
42        {
43            cfg.interceptor_state()
44                .store_append(AwsSdkFeature::EndpointOverride);
45        }
46        Ok(())
47    }
48}
49
50/// Runtime plugin that detects when a custom endpoint URL has been configured
51/// and tracks it for business metrics.
52///
53/// This plugin is created by the codegen decorator when a user explicitly
54/// sets an endpoint URL via `.endpoint_url()`. It stores the
55/// `AwsSdkFeature::EndpointOverride` feature flag in the ConfigBag for
56/// business metrics tracking.
57#[derive(Debug, Default)]
58#[non_exhaustive]
59pub struct EndpointOverrideRuntimePlugin {
60    config: Option<FrozenLayer>,
61}
62
63impl EndpointOverrideRuntimePlugin {
64    /// Creates a new `EndpointOverrideRuntimePlugin` with the given config layer
65    pub fn new(config: Option<FrozenLayer>) -> Self {
66        Self { config }
67    }
68}
69
70impl RuntimePlugin for EndpointOverrideRuntimePlugin {
71    fn config(&self) -> Option<FrozenLayer> {
72        self.config.clone()
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79    use crate::sdk_feature::AwsSdkFeature;
80    use aws_smithy_runtime_api::client::interceptors::context::{
81        BeforeSerializationInterceptorContextRef, Input, InterceptorContext,
82    };
83    use aws_smithy_types::config_bag::ConfigBag;
84
85    #[test]
86    fn test_plugin_with_no_config() {
87        let plugin = EndpointOverrideRuntimePlugin::default();
88        assert!(plugin.config().is_none());
89    }
90
91    #[test]
92    fn test_interceptor_detects_endpoint_url_when_present() {
93        let interceptor = EndpointOverrideInterceptor::new();
94        let mut cfg = ConfigBag::base();
95
96        // Set endpoint URL in config
97        let endpoint_url =
98            aws_types::endpoint_config::EndpointUrl("https://custom.example.com".to_string());
99        cfg.interceptor_state().store_put(endpoint_url);
100
101        // Create a dummy context
102        let input = Input::doesnt_matter();
103        let ctx = InterceptorContext::new(input);
104        let context = BeforeSerializationInterceptorContextRef::from(&ctx);
105
106        // Run the interceptor
107        interceptor
108            .read_before_execution(&context, &mut cfg)
109            .unwrap();
110
111        // Verify feature flag was set in interceptor_state
112        let features: Vec<_> = cfg
113            .interceptor_state()
114            .load::<AwsSdkFeature>()
115            .cloned()
116            .collect();
117        assert_eq!(features.len(), 1);
118        assert_eq!(features[0], AwsSdkFeature::EndpointOverride);
119    }
120
121    #[test]
122    fn test_interceptor_does_not_set_flag_when_endpoint_url_absent() {
123        let interceptor = EndpointOverrideInterceptor::new();
124        let mut cfg = ConfigBag::base();
125
126        // Create a dummy context
127        let input = Input::doesnt_matter();
128        let ctx = InterceptorContext::new(input);
129        let context = BeforeSerializationInterceptorContextRef::from(&ctx);
130
131        // Run the interceptor without setting endpoint URL
132        interceptor
133            .read_before_execution(&context, &mut cfg)
134            .unwrap();
135
136        // Verify no feature flag was set
137        let features: Vec<_> = cfg
138            .interceptor_state()
139            .load::<AwsSdkFeature>()
140            .cloned()
141            .collect();
142        assert_eq!(features.len(), 0);
143    }
144}