Skip to main content

launchdarkly_server_sdk/
feature_requester_builders.rs

1use crate::feature_requester::{FeatureRequester, HttpFeatureRequester};
2use crate::LAUNCHDARKLY_TAGS_HEADER;
3use http::Uri;
4use launchdarkly_sdk_transport::HttpTransport;
5use std::collections::HashMap;
6use std::str::FromStr;
7use thiserror::Error;
8
9/// Error type used to represent failures when building a [FeatureRequesterFactory] instance.
10#[non_exhaustive]
11#[derive(Debug, Error)]
12pub enum BuildError {
13    /// Error used when a configuration setting is invalid.
14    #[error("feature requester factory failed to build: {0}")]
15    InvalidConfig(String),
16}
17
18/// Trait which allows creation of feature requesters.
19///
20/// Feature requesters are used by the polling data source (see [crate::PollingDataSourceBuilder])
21/// to retrieve state information from an external resource such as the LaunchDarkly API.
22pub trait FeatureRequesterFactory: Send {
23    /// Create an instance of FeatureRequester.
24    fn build(&self, tags: Option<String>) -> Result<Box<dyn FeatureRequester>, BuildError>;
25}
26
27pub struct HttpFeatureRequesterBuilder<T: HttpTransport> {
28    url: String,
29    sdk_key: String,
30    transport: T,
31}
32
33impl<T: HttpTransport> HttpFeatureRequesterBuilder<T> {
34    pub fn new(url: &str, sdk_key: &str, transport: T) -> Self {
35        Self {
36            transport,
37            url: url.into(),
38            sdk_key: sdk_key.into(),
39        }
40    }
41}
42
43impl<T: HttpTransport> FeatureRequesterFactory for HttpFeatureRequesterBuilder<T> {
44    fn build(&self, tags: Option<String>) -> Result<Box<dyn FeatureRequester>, BuildError> {
45        let url = format!("{}/sdk/latest-all", self.url);
46
47        let mut default_headers = HashMap::<&str, String>::new();
48
49        if let Some(tags) = tags {
50            default_headers.insert(LAUNCHDARKLY_TAGS_HEADER, tags);
51        }
52
53        let url = Uri::from_str(url.as_str())
54            .map_err(|_| BuildError::InvalidConfig("Invalid base url provided".into()))?;
55
56        Ok(Box::new(HttpFeatureRequester::new(
57            self.transport.clone(),
58            url,
59            self.sdk_key.clone(),
60            default_headers,
61        )))
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68
69    #[test]
70    fn factory_handles_url_parsing_failure() {
71        let transport =
72            launchdarkly_sdk_transport::HyperTransport::new().expect("Failed to create transport");
73        let builder = HttpFeatureRequesterBuilder::new(
74            "This is clearly not a valid URL",
75            "sdk-key",
76            transport,
77        );
78        let result = builder.build(None);
79
80        match result {
81            Err(BuildError::InvalidConfig(_)) => (),
82            _ => panic!("Build did not return the right type of error"),
83        };
84    }
85}