unleash-edge 0.2.0

Unleash edge is a proxy for Unleash. It can return both evaluated feature toggles as well as the raw data from Unleash's client API
Documentation
use std::sync::Arc;

use chrono::Duration;
use reqwest::Url;

use crate::{
    auth::token_validator::TokenValidator,
    cli::{CliArgs, EdgeArg, EdgeMode, OfflineArgs},
    http::unleash_client::UnleashClient,
    types::{EdgeResult, EdgeSink, EdgeSource},
};

use super::{
    memory_provider::MemoryProvider, offline_provider::OfflineProvider,
    redis_provider::RedisProvider, repository::DataSourceFacade,
};

pub type DataProviderPair = (Arc<dyn EdgeSource>, Arc<dyn EdgeSink>);

pub struct RepositoryInfo {
    pub source: Arc<dyn EdgeSource>,
    pub sink_info: Option<SinkInfo>,
}

pub struct SinkInfo {
    pub sink: Arc<dyn EdgeSink>,
    pub unleash_client: UnleashClient,
    pub token_validator: Arc<TokenValidator>,
    pub metrics_interval_seconds: u64,
}

fn build_offline(offline_args: OfflineArgs) -> EdgeResult<Arc<dyn EdgeSource>> {
    let provider =
        OfflineProvider::instantiate_provider(offline_args.bootstrap_file, offline_args.tokens)?;

    let source: Arc<dyn EdgeSource> = Arc::new(provider);
    Ok(source)
}

fn build_memory(features_refresh_interval_seconds: Duration) -> EdgeResult<DataProviderPair> {
    let data_source = Arc::new(MemoryProvider::new());
    let facade = Arc::new(DataSourceFacade {
        features_refresh_interval: Some(features_refresh_interval_seconds),
        token_source: data_source.clone(),
        feature_source: data_source.clone(),
        token_sink: data_source.clone(),
        feature_sink: data_source,
    });

    let edge_source: Arc<dyn EdgeSource> = facade.clone();
    let edge_sink: Arc<dyn EdgeSink> = facade;

    Ok((edge_source, edge_sink))
}

fn build_redis(
    redis_url: String,
    features_refresh_interval_seconds: Duration,
) -> EdgeResult<DataProviderPair> {
    let data_source = Arc::new(RedisProvider::new(&redis_url)?);
    let facade = Arc::new(DataSourceFacade {
        token_source: data_source.clone(),
        feature_source: data_source.clone(),
        token_sink: data_source.clone(),
        feature_sink: data_source,
        features_refresh_interval: Some(features_refresh_interval_seconds),
    });

    let edge_source: Arc<dyn EdgeSource> = facade.clone();
    let edge_sink: Arc<dyn EdgeSink> = facade;

    Ok((edge_source, edge_sink))
}

pub async fn build_source_and_sink(args: CliArgs) -> EdgeResult<RepositoryInfo> {
    match args.mode {
        EdgeMode::Offline(offline_args) => {
            let source: Arc<dyn EdgeSource> = build_offline(offline_args)?;
            Ok(RepositoryInfo {
                source,
                sink_info: None,
            })
        }
        EdgeMode::Edge(edge_args) => {
            let refresh_interval = Duration::seconds(edge_args.features_refresh_interval_seconds);
            let arg: EdgeArg = edge_args.clone().into();
            let unleash_client = UnleashClient::from_url(
                Url::parse(edge_args.upstream_url.as_str()).expect("Cannot parse Upstream URL"),
            );
            let (source, sink) = match arg {
                EdgeArg::Redis(redis_url) => build_redis(redis_url, refresh_interval),
                EdgeArg::InMemory => build_memory(refresh_interval),
            }?;

            let token_validator = TokenValidator {
                unleash_client: Arc::new(unleash_client.clone()),
                edge_source: source.clone(),
                edge_sink: sink.clone(),
            };
            if !edge_args.tokens.is_empty() {
                let _ = token_validator.register_tokens(edge_args.tokens).await;
            }
            Ok(RepositoryInfo {
                source,
                sink_info: Some(SinkInfo {
                    sink,
                    unleash_client,
                    token_validator: Arc::new(token_validator),
                    metrics_interval_seconds: edge_args.metrics_interval_seconds,
                }),
            })
        }
    }
}