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)]
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 Default for EndpointOverrideRuntimePlugin {
71    fn default() -> Self {
72        Self { config: None }
73    }
74}
75
76impl RuntimePlugin for EndpointOverrideRuntimePlugin {
77    fn config(&self) -> Option<FrozenLayer> {
78        self.config.clone()
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85    use crate::sdk_feature::AwsSdkFeature;
86    use aws_smithy_runtime_api::client::interceptors::context::{
87        BeforeSerializationInterceptorContextRef, Input, InterceptorContext,
88    };
89    use aws_smithy_types::config_bag::ConfigBag;
90
91    #[test]
92    fn test_plugin_with_no_config() {
93        let plugin = EndpointOverrideRuntimePlugin::default();
94        assert!(plugin.config().is_none());
95    }
96
97    #[test]
98    fn test_interceptor_detects_endpoint_url_when_present() {
99        let interceptor = EndpointOverrideInterceptor::new();
100        let mut cfg = ConfigBag::base();
101
102        // Set endpoint URL in config
103        let endpoint_url =
104            aws_types::endpoint_config::EndpointUrl("https://custom.example.com".to_string());
105        cfg.interceptor_state().store_put(endpoint_url);
106
107        // Create a dummy context
108        let input = Input::doesnt_matter();
109        let ctx = InterceptorContext::new(input);
110        let context = BeforeSerializationInterceptorContextRef::from(&ctx);
111
112        // Run the interceptor
113        interceptor
114            .read_before_execution(&context, &mut cfg)
115            .unwrap();
116
117        // Verify feature flag was set in interceptor_state
118        let features: Vec<_> = cfg
119            .interceptor_state()
120            .load::<AwsSdkFeature>()
121            .cloned()
122            .collect();
123        assert_eq!(features.len(), 1);
124        assert_eq!(features[0], AwsSdkFeature::EndpointOverride);
125    }
126
127    #[test]
128    fn test_interceptor_does_not_set_flag_when_endpoint_url_absent() {
129        let interceptor = EndpointOverrideInterceptor::new();
130        let mut cfg = ConfigBag::base();
131
132        // Create a dummy context
133        let input = Input::doesnt_matter();
134        let ctx = InterceptorContext::new(input);
135        let context = BeforeSerializationInterceptorContextRef::from(&ctx);
136
137        // Run the interceptor without setting endpoint URL
138        interceptor
139            .read_before_execution(&context, &mut cfg)
140            .unwrap();
141
142        // Verify no feature flag was set
143        let features: Vec<_> = cfg
144            .interceptor_state()
145            .load::<AwsSdkFeature>()
146            .cloned()
147            .collect();
148        assert_eq!(features.len(), 0);
149    }
150}