Skip to content
Merged
32 changes: 25 additions & 7 deletions launchdarkly-server-sdk/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use tokio::sync::{broadcast, Semaphore};
use super::config::Config;
use super::data_source_builders::BuildError as DataSourceError;
use super::data_system::{DataSystem, FDv1DataSystem};
use super::data_system_builders::BuildError as DataSystemError;
use super::evaluation::{FlagDetail, FlagDetailConfig};
use super::stores::store::DataStore;
use super::stores::store_builders::BuildError as DataStoreError;
Expand Down Expand Up @@ -65,6 +66,12 @@ impl From<DataSourceError> for BuildError {
}
}

impl From<DataSystemError> for BuildError {
fn from(error: DataSystemError) -> Self {
Self::InvalidConfig(error.to_string())
}
}

impl From<DataStoreError> for BuildError {
fn from(error: DataStoreError) -> Self {
Self::InvalidConfig(error.to_string())
Expand Down Expand Up @@ -184,13 +191,24 @@ impl Client {
let event_processor =
event_processor_builder.build(&endpoints, config.sdk_key(), tags.clone())?;

let mut data_source_builder = config.data_source_builder().to_owned();
data_source_builder.set_instance_id(instance_id);
let data_source = data_source_builder.build(&endpoints, config.sdk_key(), tags.clone())?;
let data_system: Arc<dyn DataSystem> = Arc::new(FDv1DataSystem::new(
data_source,
config.data_store_builder(),
)?);
let data_system: Arc<dyn DataSystem> = match config.data_system_builder() {
Some(data_system_builder) => data_system_builder.build(
&endpoints,
config.sdk_key(),
tags.as_deref(),
&instance_id,
)?,
None => {
let mut data_source_builder = config.data_source_builder().to_owned();
data_source_builder.set_instance_id(instance_id);
let data_source =
data_source_builder.build(&endpoints, config.sdk_key(), tags.clone())?;
Arc::new(FDv1DataSystem::new(
data_source,
config.data_store_builder(),
)?)
}
};
let data_store = data_system.store();

let events_default = EventsScope {
Expand Down
39 changes: 39 additions & 0 deletions launchdarkly-server-sdk/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use thiserror::Error;

use crate::data_source_builders::{DataSourceFactory, NullDataSourceBuilder};
use crate::data_system_builders::{DataSystemBuilder, DataSystemFactory};

#[cfg(any(
feature = "hyper-rustls-native-roots",
Expand Down Expand Up @@ -136,6 +137,7 @@ pub struct Config {
service_endpoints_builder: ServiceEndpointsBuilder,
data_store_builder: Box<dyn DataStoreFactory>,
data_source_builder: Box<dyn DataSourceFactory>,
data_system_builder: Option<Box<dyn DataSystemFactory>>,
event_processor_builder: Box<dyn EventProcessorFactory>,
application_tag: Option<String>,
instance_id: String,
Expand Down Expand Up @@ -164,6 +166,11 @@ impl Config {
self.data_source_builder.borrow()
}

/// Returns the DataSystemFactory, if an FDv2 data system was configured.
pub(crate) fn data_system_builder(&self) -> Option<&dyn DataSystemFactory> {
self.data_system_builder.as_deref()
}

/// Returns the EventProcessorFactory
pub fn event_processor_builder(&self) -> &dyn EventProcessorFactory {
self.event_processor_builder.borrow()
Expand Down Expand Up @@ -212,6 +219,7 @@ pub struct ConfigBuilder {
service_endpoints_builder: Option<ServiceEndpointsBuilder>,
data_store_builder: Option<Box<dyn DataStoreFactory>>,
data_source_builder: Option<Box<dyn DataSourceFactory>>,
data_system_builder: Option<Box<dyn DataSystemFactory>>,
event_processor_builder: Option<Box<dyn EventProcessorFactory>>,
application_info: Option<ApplicationInfo>,
offline: bool,
Expand All @@ -226,6 +234,7 @@ impl ConfigBuilder {
service_endpoints_builder: None,
data_store_builder: None,
data_source_builder: None,
data_system_builder: None,
event_processor_builder: None,
offline: false,
daemon_mode: false,
Expand Down Expand Up @@ -258,6 +267,16 @@ impl ConfigBuilder {
self
}

/// Set the data system to use for this client.
///
/// When set, the data system supersedes the [data_source](ConfigBuilder::data_source).
/// If offline mode is enabled, it will be ignored.
pub fn data_system(mut self, builder: &DataSystemBuilder) -> Self {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the builder is a concrete type, you could decide to take ownership over the builder rather than passing it in by reference.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did it this way to be consistent with the existing service_endpoints API, which is a concrete type, but takes its builder by reference and clones it like this. Does that change your suggestion, or do you still think it's worth doing?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, it's probably better to stay consistent. At some point, we should revisit the ownership model of this repository, but happy to stay the course for now.

let factory: Box<dyn DataSystemFactory> = Box::new(builder.clone());
self.data_system_builder = Some(factory);
self
}

/// Set the event processor to use for this client.
/// For usage see [EventProcessorBuilder](crate::EventProcessorBuilder).
///
Expand Down Expand Up @@ -307,8 +326,27 @@ impl ConfigBuilder {
Some(_data_store_builder) => self.data_store_builder.unwrap(),
};

// The data system is optional; when set it supersedes the data source.
// Like the data source, it is ignored in offline or daemon mode.
let data_system_builder = match self.data_system_builder {
Some(_) if self.offline => {
warn!("Custom data system builders will be ignored when in offline mode");
None
}
Some(_) if self.daemon_mode => {
warn!("Custom data system builders will be ignored when in daemon mode");
None
}
other => other,
};

let data_source_builder_result: Result<Box<dyn DataSourceFactory>, BuildError> =
match self.data_source_builder {
None if data_system_builder.is_some() => Ok(Box::new(NullDataSourceBuilder::new())),
Some(_) if data_system_builder.is_some() => {
warn!("Custom data source builders will be ignored when a data system is configured");
Ok(Box::new(NullDataSourceBuilder::new()))
}
None if self.offline => Ok(Box::new(NullDataSourceBuilder::new())),
Some(_) if self.offline => {
warn!("Custom data source builders will be ignored when in offline mode");
Expand Down Expand Up @@ -401,6 +439,7 @@ impl ConfigBuilder {
service_endpoints_builder,
data_store_builder,
data_source_builder,
data_system_builder,
event_processor_builder,
application_tag,
instance_id,
Expand Down
16 changes: 16 additions & 0 deletions launchdarkly-server-sdk/src/data_sources.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//! Types for implementing custom FDv2 data sources.
//!
//! This module is experimental and not subject to semantic versioning. Its API
//! may change in any release.

pub use crate::data_system_builders::{
DataSourceBuildContext, FDv2InitializerConfig, FDv2SynchronizerConfig,
};
pub use crate::fdv2::data_system::{InitializerFactory, SynchronizerFactory};
pub use crate::fdv2::model::{ChangeSetKind, Selector};
pub use crate::fdv2::request_headers::RequestHeaders;
pub use crate::fdv2::source::{
ErrorInfo, ErrorKind, FDv1FallbackDirective, FDv2SourceEvent, FDv2SourceResult, Initializer,
Synchronizer,
};
pub use crate::stores::change_set::{ChangeSet, ItemChange};
Loading
Loading