launchdarkly_server_sdk/
service_endpoints.rs1use super::client::BuildError;
2
3const DEFAULT_POLLING_BASE_URL: &str = "https://sdk.launchdarkly.com";
4const DEFAULT_STREAM_BASE_URL: &str = "https://stream.launchdarkly.com";
5const DEFAULT_EVENTS_BASE_URL: &str = "https://events.launchdarkly.com";
6
7pub struct ServiceEndpoints {
9 polling_base_url: String,
10 streaming_base_url: String,
11 events_base_url: String,
12}
13
14impl ServiceEndpoints {
15 pub fn polling_base_url(&self) -> &str {
16 self.polling_base_url.as_ref()
17 }
18
19 pub fn streaming_base_url(&self) -> &str {
20 self.streaming_base_url.as_ref()
21 }
22
23 pub fn events_base_url(&self) -> &str {
24 self.events_base_url.as_ref()
25 }
26}
27
28#[derive(Clone)]
69pub struct ServiceEndpointsBuilder {
70 polling_base_url: Option<String>,
71 streaming_base_url: Option<String>,
72 events_base_url: Option<String>,
73}
74
75impl ServiceEndpointsBuilder {
76 pub fn new() -> ServiceEndpointsBuilder {
78 ServiceEndpointsBuilder {
79 polling_base_url: None,
80 streaming_base_url: None,
81 events_base_url: None,
82 }
83 }
84
85 pub fn polling_base_url(&mut self, url: &str) -> &mut Self {
87 self.polling_base_url = Some(String::from(url));
88 self
89 }
90
91 pub fn streaming_base_url(&mut self, url: &str) -> &mut Self {
93 self.streaming_base_url = Some(String::from(url));
94 self
95 }
96
97 pub fn events_base_url(&mut self, url: &str) -> &mut Self {
99 self.events_base_url = Some(String::from(url));
100 self
101 }
102
103 pub fn relay_proxy(&mut self, url: &str) -> &mut Self {
105 self.polling_base_url = Some(String::from(url));
106 self.streaming_base_url = Some(String::from(url));
107 self.events_base_url = Some(String::from(url));
108 self
109 }
110
111 pub fn build(&self) -> Result<ServiceEndpoints, BuildError> {
121 match (
122 &self.polling_base_url,
123 &self.streaming_base_url,
124 &self.events_base_url,
125 ) {
126 (Some(polling_base_url), Some(streaming_base_url), Some(events_base_url)) => {
127 Ok(ServiceEndpoints {
128 polling_base_url: polling_base_url.trim_end_matches('/').to_string(),
129 streaming_base_url: streaming_base_url.trim_end_matches('/').to_string(),
130 events_base_url: events_base_url.trim_end_matches('/').to_string(),
131 })
132 }
133 (None, None, None) => Ok(ServiceEndpoints {
134 polling_base_url: String::from(DEFAULT_POLLING_BASE_URL),
135 streaming_base_url: String::from(DEFAULT_STREAM_BASE_URL),
136 events_base_url: String::from(DEFAULT_EVENTS_BASE_URL),
137 }),
138 _ => Err(BuildError::InvalidConfig(
139 "If you specify any endpoints,\
140 then you must specify all endpoints."
141 .into(),
142 )),
143 }
144 }
145}
146
147impl Default for ServiceEndpointsBuilder {
148 fn default() -> Self {
149 Self::new()
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use super::*;
156 use test_case::test_case;
157
158 #[test_case("localhost", "localhost"; "requires no trimming")]
159 #[test_case("http://localhost", "http://localhost"; "requires no trimming with scheme")]
160 #[test_case("localhost/", "localhost"; "trims trailing slash")]
161 #[test_case("http://localhost/", "http://localhost"; "trims trailing slash with scheme")]
162 #[test_case("localhost////////", "localhost"; "trims multiple trailing slashes")]
163 fn service_endpoints_trims_base_urls(url: &str, expected: &str) {
164 let endpoints = ServiceEndpointsBuilder::new()
165 .polling_base_url(url)
166 .streaming_base_url(url)
167 .events_base_url(url)
168 .build()
169 .expect("Provided URLs should parse successfully");
170
171 assert_eq!(expected, endpoints.events_base_url());
172 assert_eq!(expected, endpoints.streaming_base_url());
173 assert_eq!(expected, endpoints.polling_base_url());
174 }
175
176 #[test]
177 fn default_configuration() -> Result<(), BuildError> {
178 let endpoints = ServiceEndpointsBuilder::new().build()?;
179 assert_eq!(
180 "https://events.launchdarkly.com",
181 endpoints.events_base_url()
182 );
183 assert_eq!(
184 "https://stream.launchdarkly.com",
185 endpoints.streaming_base_url()
186 );
187 assert_eq!("https://sdk.launchdarkly.com", endpoints.polling_base_url());
188 Ok(())
189 }
190
191 #[test]
192 fn full_custom_configuration() -> Result<(), BuildError> {
193 let endpoints = ServiceEndpointsBuilder::new()
194 .polling_base_url("https://sdk.my-private-instance.com")
195 .streaming_base_url("https://stream.my-private-instance.com")
196 .events_base_url("https://events.my-private-instance.com")
197 .build()?;
198
199 assert_eq!(
200 "https://events.my-private-instance.com",
201 endpoints.events_base_url()
202 );
203 assert_eq!(
204 "https://stream.my-private-instance.com",
205 endpoints.streaming_base_url()
206 );
207 assert_eq!(
208 "https://sdk.my-private-instance.com",
209 endpoints.polling_base_url()
210 );
211 Ok(())
212 }
213
214 #[test]
215 fn partial_definition() {
216 assert!(ServiceEndpointsBuilder::new()
217 .polling_base_url("https://sdk.my-private-instance.com")
218 .build()
219 .is_err());
220 }
221
222 #[test]
223 fn configure_relay_proxy() -> Result<(), BuildError> {
224 let endpoints = ServiceEndpointsBuilder::new()
225 .relay_proxy("http://my-relay-hostname:8080")
226 .build()?;
227 assert_eq!("http://my-relay-hostname:8080", endpoints.events_base_url());
228 assert_eq!(
229 "http://my-relay-hostname:8080",
230 endpoints.streaming_base_url()
231 );
232 assert_eq!(
233 "http://my-relay-hostname:8080",
234 endpoints.polling_base_url()
235 );
236 Ok(())
237 }
238}