From 7b64e64b2ce4470e5fcd11b27beeb35439189c80 Mon Sep 17 00:00:00 2001 From: "ayush.jain@juspay.in" Date: Sat, 11 Apr 2026 17:42:28 +0530 Subject: [PATCH] fix(python): Add missing SuperpositionDataSource implementation for LocalResolutionProvider --- .../superposition_client.kt | 33 +++++- .../superposition_client.py | 33 +++++- .../superposition_provider/data_source.py | 3 +- .../file_data_source.py | 46 ++------ .../http_data_source.py | 48 +++----- .../superposition_provider/local_provider.py | 107 +++++++++++++++++- crates/superposition_core/src/ffi.rs | 24 +++- .../superposition_provider/src/data_source.rs | 5 +- .../src/data_source/file.rs | 31 ++--- .../src/data_source/http.rs | 26 +---- .../src/local_provider.rs | 41 +++---- 11 files changed, 238 insertions(+), 159 deletions(-) diff --git a/clients/java/bindings/src/main/kotlin/uniffi/superposition_client/superposition_client.kt b/clients/java/bindings/src/main/kotlin/uniffi/superposition_client/superposition_client.kt index 7a8a62516..0e2840024 100644 --- a/clients/java/bindings/src/main/kotlin/uniffi/superposition_client/superposition_client.kt +++ b/clients/java/bindings/src/main/kotlin/uniffi/superposition_client/superposition_client.kt @@ -877,7 +877,7 @@ fun uniffi_superposition_core_fn_method_providercache_eval_config(`ptr`: Pointer ): RustBuffer.ByValue fun uniffi_superposition_core_fn_method_providercache_filter_config(`ptr`: Pointer,`dimensionData`: RustBuffer.ByValue,`prefix`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, ): RustBufferConfig.ByValue -fun uniffi_superposition_core_fn_method_providercache_filter_experiment(`ptr`: Pointer,`dimensionData`: RustBuffer.ByValue,`prefix`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +fun uniffi_superposition_core_fn_method_providercache_filter_experiment(`ptr`: Pointer,`dimensionData`: RustBuffer.ByValue,`prefix`: RustBuffer.ByValue,`partialApply`: Byte,uniffi_out_err: UniffiRustCallStatus, ): RustBuffer.ByValue fun uniffi_superposition_core_fn_method_providercache_get_applicable_variants(`ptr`: Pointer,`dimensionData`: RustBuffer.ByValue,`prefix`: RustBuffer.ByValue,`targetingKey`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, ): RustBuffer.ByValue @@ -1047,7 +1047,7 @@ private fun uniffiCheckApiChecksums(lib: IntegrityCheckingUniffiLib) { if (lib.uniffi_superposition_core_checksum_method_providercache_filter_config() != 21761.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_superposition_core_checksum_method_providercache_filter_experiment() != 60575.toShort()) { + if (lib.uniffi_superposition_core_checksum_method_providercache_filter_experiment() != 31120.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } if (lib.uniffi_superposition_core_checksum_method_providercache_get_applicable_variants() != 12269.toShort()) { @@ -1228,6 +1228,29 @@ public object FfiConverterUByte: FfiConverter { } } +/** + * @suppress + */ +public object FfiConverterBoolean: FfiConverter { + override fun lift(value: Byte): Boolean { + return value.toInt() != 0 + } + + override fun read(buf: ByteBuffer): Boolean { + return lift(buf.get()) + } + + override fun lower(value: Boolean): Byte { + return if (value) 1.toByte() else 0.toByte() + } + + override fun allocationSize(value: Boolean) = 1UL + + override fun write(value: Boolean, buf: ByteBuffer) { + buf.put(lower(value)) + } +} + /** * @suppress */ @@ -1390,7 +1413,7 @@ public interface ProviderCacheInterface { fun `filterConfig`(`dimensionData`: Map?, `prefix`: List?): Config - fun `filterExperiment`(`dimensionData`: Map?, `prefix`: List?): ExperimentConfig + fun `filterExperiment`(`dimensionData`: Map?, `prefix`: List?, `partialApply`: kotlin.Boolean): ExperimentConfig fun `getApplicableVariants`(`dimensionData`: Map?, `prefix`: List?, `targetingKey`: kotlin.String): List @@ -1517,12 +1540,12 @@ open class ProviderCache: Disposable, AutoCloseable, ProviderCacheInterface - @Throws(OperationException::class)override fun `filterExperiment`(`dimensionData`: Map?, `prefix`: List?): ExperimentConfig { + @Throws(OperationException::class)override fun `filterExperiment`(`dimensionData`: Map?, `prefix`: List?, `partialApply`: kotlin.Boolean): ExperimentConfig { return FfiConverterTypeExperimentConfig.lift( callWithPointer { uniffiRustCallWithError(OperationException) { _status -> UniffiLib.INSTANCE.uniffi_superposition_core_fn_method_providercache_filter_experiment( - it, FfiConverterOptionalMapStringString.lower(`dimensionData`),FfiConverterOptionalSequenceString.lower(`prefix`),_status) + it, FfiConverterOptionalMapStringString.lower(`dimensionData`),FfiConverterOptionalSequenceString.lower(`prefix`),FfiConverterBoolean.lower(`partialApply`),_status) } } ) diff --git a/clients/python/bindings/superposition_bindings/superposition_client.py b/clients/python/bindings/superposition_bindings/superposition_client.py index 2a8bed303..fd077c564 100644 --- a/clients/python/bindings/superposition_bindings/superposition_client.py +++ b/clients/python/bindings/superposition_bindings/superposition_client.py @@ -511,7 +511,7 @@ def _uniffi_check_api_checksums(lib): raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") if lib.uniffi_superposition_core_checksum_method_providercache_filter_config() != 21761: raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - if lib.uniffi_superposition_core_checksum_method_providercache_filter_experiment() != 60575: + if lib.uniffi_superposition_core_checksum_method_providercache_filter_experiment() != 31120: raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") if lib.uniffi_superposition_core_checksum_method_providercache_get_applicable_variants() != 12269: raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") @@ -661,6 +661,7 @@ class _UniffiForeignFutureStructVoid(ctypes.Structure): ctypes.c_void_p, _UniffiRustBuffer, _UniffiRustBuffer, + ctypes.c_int8, ctypes.POINTER(_UniffiRustCallStatus), ) _UniffiLib.uniffi_superposition_core_fn_method_providercache_filter_experiment.restype = _UniffiRustBuffer @@ -1068,6 +1069,27 @@ def read(buf): def write(value, buf): buf.write_u8(value) +class _UniffiConverterBool: + @classmethod + def check_lower(cls, value): + return not not value + + @classmethod + def lower(cls, value): + return 1 if value else 0 + + @staticmethod + def lift(value): + return value != 0 + + @classmethod + def read(cls, buf): + return cls.lift(buf.read_u8()) + + @classmethod + def write(cls, value, buf): + buf.write_u8(value) + class _UniffiConverterString: @staticmethod def check_lower(value): @@ -1747,7 +1769,7 @@ def eval_config(self, query_data: "dict[str, str]",merge_strategy: "MergeStrateg raise NotImplementedError def filter_config(self, dimension_data: "typing.Optional[dict[str, str]]",prefix: "typing.Optional[typing.List[str]]"): raise NotImplementedError - def filter_experiment(self, dimension_data: "typing.Optional[dict[str, str]]",prefix: "typing.Optional[typing.List[str]]"): + def filter_experiment(self, dimension_data: "typing.Optional[dict[str, str]]",prefix: "typing.Optional[typing.List[str]]",partial_apply: "bool"): raise NotImplementedError def get_applicable_variants(self, dimension_data: "typing.Optional[dict[str, str]]",prefix: "typing.Optional[typing.List[str]]",targeting_key: "str"): raise NotImplementedError @@ -1816,15 +1838,18 @@ def filter_config(self, dimension_data: "typing.Optional[dict[str, str]]",prefix - def filter_experiment(self, dimension_data: "typing.Optional[dict[str, str]]",prefix: "typing.Optional[typing.List[str]]") -> "ExperimentConfig": + def filter_experiment(self, dimension_data: "typing.Optional[dict[str, str]]",prefix: "typing.Optional[typing.List[str]]",partial_apply: "bool") -> "ExperimentConfig": _UniffiConverterOptionalMapStringString.check_lower(dimension_data) _UniffiConverterOptionalSequenceString.check_lower(prefix) + _UniffiConverterBool.check_lower(partial_apply) + return _UniffiConverterTypeExperimentConfig.lift( _uniffi_rust_call_with_error(_UniffiConverterTypeOperationError,_UniffiLib.uniffi_superposition_core_fn_method_providercache_filter_experiment,self._uniffi_clone_pointer(), _UniffiConverterOptionalMapStringString.lower(dimension_data), - _UniffiConverterOptionalSequenceString.lower(prefix)) + _UniffiConverterOptionalSequenceString.lower(prefix), + _UniffiConverterBool.lower(partial_apply)) ) diff --git a/clients/python/provider/superposition_provider/data_source.py b/clients/python/provider/superposition_provider/data_source.py index a6fa7660b..263b177f9 100644 --- a/clients/python/provider/superposition_provider/data_source.py +++ b/clients/python/provider/superposition_provider/data_source.py @@ -77,7 +77,6 @@ class SuperpositionDataSource(ABC): while consumers interact with this unified interface. """ - @abstractmethod async def fetch_config( self, if_modified_since: Optional[datetime] = None, @@ -90,7 +89,7 @@ async def fetch_config( Returns: FetchResponse with ConfigData or NotModified status. """ - pass + return await self.fetch_filtered_config(if_modified_since=if_modified_since) @abstractmethod async def fetch_filtered_config( diff --git a/clients/python/provider/superposition_provider/file_data_source.py b/clients/python/provider/superposition_provider/file_data_source.py index 10f4512a6..70a9f69fe 100644 --- a/clients/python/provider/superposition_provider/file_data_source.py +++ b/clients/python/provider/superposition_provider/file_data_source.py @@ -100,17 +100,24 @@ def __init__( else: raise ValueError(f"Unsupported file format: {file_path}") - async def _fetch_config_with_filters( + async def fetch_filtered_config( self, context: Optional[Dict[str, Any]] = None, prefix_filter: Optional[List[str]] = None, if_modified_since: Optional[datetime] = None, ) -> FetchResponse[ConfigData]: - """Fetch configuration from file, applying filters and 304 Not Modified logic. + """Fetch configuration, optionally filtered. + + Note: File-based filtering is not efficient; consider using HttpDataSource + for production configurations that need filtering. + Args: context: Optional context for filtering (ignored). prefix_filter: Optional key prefixes to include. if_modified_since: Timestamp for 304 Not Modified check. + + Returns: + FetchResponse with ConfigData or NotModified status. """ if if_modified_since is not None: logger.debug("FileDataSource: ignoring if_modified_since, always reading fresh from file") @@ -131,41 +138,6 @@ async def _fetch_config_with_filters( logger.error(f"Failed to fetch config from {self.file_path}: {e}") raise - async def fetch_config( - self, - if_modified_since: Optional[datetime] = None, - ) -> FetchResponse[ConfigData]: - """Fetch configuration from file. - - Args: - if_modified_since: Timestamp for 304 Not Modified check. - - Returns: - FetchResponse with ConfigData or NotModified status. - """ - return await self._fetch_config_with_filters(if_modified_since=if_modified_since) - - async def fetch_filtered_config( - self, - context: Optional[Dict[str, Any]] = None, - prefix_filter: Optional[List[str]] = None, - if_modified_since: Optional[datetime] = None, - ) -> FetchResponse[ConfigData]: - """Fetch configuration, optionally filtered. - - Note: File-based filtering is not efficient; consider using HttpDataSource - for production configurations that need filtering. - - Args: - context: Optional context for filtering (ignored). - prefix_filter: Optional key prefixes to include. - if_modified_since: Timestamp for 304 Not Modified check. - - Returns: - FetchResponse with ConfigData or NotModified status. - """ - return await self._fetch_config_with_filters(context, prefix_filter, if_modified_since) - async def fetch_active_experiments( self, if_modified_since: Optional[datetime] = None, diff --git a/clients/python/provider/superposition_provider/http_data_source.py b/clients/python/provider/superposition_provider/http_data_source.py index 8166b8257..7b6e3974b 100644 --- a/clients/python/provider/superposition_provider/http_data_source.py +++ b/clients/python/provider/superposition_provider/http_data_source.py @@ -62,12 +62,22 @@ def _create_client(self) -> Superposition: # Create Superposition client return Superposition(config=sdk_config) - async def _fetch_config_with_filters( + async def fetch_filtered_config( self, context: Optional[Dict[str, Any]] = None, prefix_filter: Optional[List[str]] = None, - if_modified_since: Optional[datetime] = None + if_modified_since: Optional[datetime] = None, ) -> FetchResponse[ConfigData]: + """Fetch resolved configuration filtered by context and prefixes. + + Args: + context: Optional context for filtering. + prefix_filter: Optional list of key prefixes to include. + if_modified_since: Optional timestamp for 304 Not Modified check. + + Returns: + FetchResponse with ConfigData or NotModified status. + """ try: context = {k: Document(v) for k, v in context.items()} if context else None response = await self.client.get_config( @@ -89,38 +99,6 @@ async def _fetch_config_with_filters( except Exception as e: raise e - async def fetch_config( - self, - if_modified_since: Optional[datetime] = None, - ) -> FetchResponse: - """Fetch full resolved configuration. - - Args: - if_modified_since: Optional timestamp for 304 Not Modified check. - - Returns: - FetchResponse with ConfigData or NotModified status. - """ - return await self._fetch_config_with_filters(if_modified_since=if_modified_since) - - async def fetch_filtered_config( - self, - context: Optional[Dict[str, Any]] = None, - prefix_filter: Optional[List[str]] = None, - if_modified_since: Optional[datetime] = None, - ) -> FetchResponse: - """Fetch resolved configuration filtered by context and prefixes. - - Args: - context: Optional context for filtering. - prefix_filter: Optional list of key prefixes to include. - if_modified_since: Optional timestamp for 304 Not Modified check. - - Returns: - FetchResponse with ConfigData or NotModified status. - """ - return await self._fetch_config_with_filters(context, prefix_filter, if_modified_since) - async def _fetch_filtered_experiment( self, context: Optional[Dict[str, Any]] = None, @@ -173,7 +151,7 @@ async def fetch_candidate_active_experiments( context: Optional[Dict[str, Any]] = None, prefix_filter: Optional[List[str]] = None, if_modified_since: Optional[datetime] = None, - ) -> FetchResponse: + ) -> FetchResponse[ExperimentData]: """Fetch active experiments with candidate conditions. Args: diff --git a/clients/python/provider/superposition_provider/local_provider.py b/clients/python/provider/superposition_provider/local_provider.py index 66c5f9fd1..1f24286ef 100644 --- a/clients/python/provider/superposition_provider/local_provider.py +++ b/clients/python/provider/superposition_provider/local_provider.py @@ -22,13 +22,14 @@ from superposition_bindings.superposition_client import ProviderCache from superposition_bindings.superposition_types import MergeStrategy +from . import FetchResponse from .data_source import SuperpositionDataSource, ConfigData, ExperimentData from .interfaces import AllFeatureProvider, FeatureExperimentMeta from .types import RefreshStrategy, OnDemandStrategy, WatchStrategy, PollingStrategy, ManualStrategy, default_on_demand_strategy logger = logging.getLogger(__name__) -class LocalResolutionProvider(AbstractProvider, AllFeatureProvider, FeatureExperimentMeta): +class LocalResolutionProvider(AbstractProvider, AllFeatureProvider, FeatureExperimentMeta, SuperpositionDataSource): """Local in-process OpenFeature provider with caching and refresh strategies. Features: @@ -531,3 +532,107 @@ def _update_exp_ffi_cache(self) -> None: """Update ffi exp config cache with new values.""" exp = self.cached_experiments.data self.ffi_cache.init_experiments(exp.experiments, exp.experiment_groups) + + + async def fetch_filtered_config( + self, + context: Optional[Dict[str, Any]] = None, + prefix_filter: Optional[List[str]] = None, + if_modified_since: Optional[datetime] = None, + ) -> FetchResponse[ConfigData]: + """Fetch configuration, optionally filtered. + + Note: File-based filtering is not efficient; consider using HttpDataSource + for production configurations that need filtering. + + Args: + context: Optional context for filtering (ignored). + prefix_filter: Optional key prefixes to include. + if_modified_since: Timestamp for 304 Not Modified check. + + Returns: + FetchResponse with ConfigData or NotModified status. + """ + if not self.ffi_cache or not self.cached_config: + raise RuntimeError("Provider not properly initialized or no config available") + + if if_modified_since is not None: + logger.debug("LocalResolutionProvider: ignoring if_modified_since, always reading fresh from file") + + return FetchResponse.data(ConfigData( + data=self.ffi_cache.filter_config(context, prefix_filter), + fetched_at=self.cached_config.fetched_at, + )) + + async def fetch_active_experiments( + self, + if_modified_since: Optional[datetime] = None, + ) -> FetchResponse[ExperimentData]: + """Fetch experiments from file. + + Args: + if_modified_since: Timestamp for 304 Not Modified check. + + Returns: + FetchResponse with ExperimentData or NotModified status. + """ + if not self.supports_experiments(): + raise NotImplementedError("Experiments not supported by this provider") + + if not self.cached_experiments: + raise RuntimeError("Provider not properly initialized or no experiments available") + + if if_modified_since is not None: + logger.debug("LocalResolutionProvider: ignoring if_modified_since for experiments, always returning cached data") + + return FetchResponse.data(self.cached_experiments) + + async def fetch_candidate_active_experiments( + self, + context: Optional[Dict[str, Any]] = None, + prefix_filter: Optional[List[str]] = None, + if_modified_since: Optional[datetime] = None, + ) -> FetchResponse[ExperimentData]: + """Fetch candidate active experiments.""" + if not self.supports_experiments(): + raise NotImplementedError("Experiments not supported by this provider") + + if not self.ffi_cache or not self.cached_experiments: + raise RuntimeError("Provider not properly initialized or no experiments available") + + if if_modified_since is not None: + logger.debug("LocalResolutionProvider: ignoring if_modified_since for experiments, always returning cached data") + + return FetchResponse.data(ExperimentData( + data=self.ffi_cache.filter_experiment(context, prefix_filter, False), + fetched_at=self.cached_experiments.fetched_at, + )) + + async def fetch_matching_active_experiments( + self, + context: Optional[Dict[str, Any]] = None, + prefix_filter: Optional[List[str]] = None, + if_modified_since: Optional[datetime] = None, + ) -> FetchResponse[ExperimentData]: + """Fetch matching active experiments.""" + if not self.supports_experiments(): + raise NotImplementedError("Experiments not supported by this provider") + + if not self.ffi_cache or not self.cached_experiments: + raise RuntimeError("Provider not properly initialized or no experiments available") + + if if_modified_since is not None: + logger.debug("LocalResolutionProvider: ignoring if_modified_since for experiments, always returning cached data") + + return FetchResponse.data(ExperimentData( + data=self.ffi_cache.filter_experiment(context, prefix_filter, True), + fetched_at=self.cached_experiments.fetched_at, + )) + + def supports_experiments(self) -> bool: + """File source supports experiments if path is configured.""" + return self.primary_source.supports_experiments() + + async def close(self) -> None: + """Stop watching and clean up resources.""" + return await self.shutdown() diff --git a/crates/superposition_core/src/ffi.rs b/crates/superposition_core/src/ffi.rs index 0dc88c6dc..a3f67cd0d 100644 --- a/crates/superposition_core/src/ffi.rs +++ b/crates/superposition_core/src/ffi.rs @@ -6,7 +6,9 @@ use superposition_types::experimental::Experimental; use superposition_types::{Config, Context, DimensionInfo, Overrides}; use thiserror::Error; -use crate::experiment::{filter_experiments_by_context, ExperimentConfig}; +use crate::experiment::{ + filter_experiments_by_context, get_satisfied_experiments, ExperimentConfig, +}; use crate::{ eval_config, eval_config_with_reasoning, experiment::ExperimentationArgs, experiment::FfiExperimentGroup, get_applicable_variants, ConfigFormat, FfiExperiment, @@ -389,6 +391,7 @@ impl ProviderCache { &self, dimension_data: Option>, prefix: Option>, + partial_apply: bool, ) -> Result { let dimension_data = dimension_data .map(json_from_map) @@ -416,12 +419,21 @@ impl ProviderCache { ) }; + let exp_filter_fn = if partial_apply { + filter_experiments_by_context + } else { + get_satisfied_experiments + }; + + let exp_grp_filter_fn = if partial_apply { + FfiExperimentGroup::filter_by_eval + } else { + FfiExperimentGroup::get_satisfied + }; + Ok(ExperimentConfig { - experiments: filter_experiments_by_context(exps, &dimension_data, prefix), - experiment_groups: FfiExperimentGroup::filter_by_eval( - exp_grps, - &dimension_data, - ), + experiments: exp_filter_fn(exps, &dimension_data, prefix), + experiment_groups: exp_grp_filter_fn(exp_grps, &dimension_data), }) } diff --git a/crates/superposition_provider/src/data_source.rs b/crates/superposition_provider/src/data_source.rs index 3a6d8cf37..76475a225 100644 --- a/crates/superposition_provider/src/data_source.rs +++ b/crates/superposition_provider/src/data_source.rs @@ -102,7 +102,10 @@ pub trait SuperpositionDataSource: Send + Sync { async fn fetch_config( &self, if_modified_since: Option>, - ) -> Result>; + ) -> Result> { + self.fetch_filtered_config(None, None, if_modified_since) + .await + } /// Fetch a resolved configuration filtered by the given context and key prefixes. async fn fetch_filtered_config( diff --git a/crates/superposition_provider/src/data_source/file.rs b/crates/superposition_provider/src/data_source/file.rs index 6aa350492..2adfdbad9 100644 --- a/crates/superposition_provider/src/data_source/file.rs +++ b/crates/superposition_provider/src/data_source/file.rs @@ -51,8 +51,10 @@ impl FileDataSource { #[async_trait] impl SuperpositionDataSource for FileDataSource { - async fn fetch_config( + async fn fetch_filtered_config( &self, + context: Option>, + prefix_filter: Option>, if_modified_since: Option>, ) -> Result> { if if_modified_since.is_some() { @@ -72,7 +74,7 @@ impl SuperpositionDataSource for FileDataSource { "json" => JsonFormat::parse_config, _ => TomlFormat::parse_config, }; - let config = parser(&content).map_err(|e| { + let mut config = parser(&content).map_err(|e| { SuperpositionError::ConfigError(format!( "Failed to parse {} config: {}", self.file_format.to_uppercase(), @@ -80,32 +82,17 @@ impl SuperpositionDataSource for FileDataSource { )) })?; + config = config.filter( + context.as_ref(), + prefix_filter.map(|p| p.into_iter().collect()).as_ref(), + ); + Ok(FetchResponse::Data(ConfigData { data: config, fetched_at: now, })) } - async fn fetch_filtered_config( - &self, - context: Option>, - prefix_filter: Option>, - if_modified_since: Option>, - ) -> Result> { - let resp = self - .fetch_config(if_modified_since) - .await? - .map_data(|mut data| { - data.data = data.data.filter( - context.as_ref(), - prefix_filter.map(|p| p.into_iter().collect()).as_ref(), - ); - data - }); - - Ok(resp) - } - async fn fetch_active_experiments( &self, _if_modified_since: Option>, diff --git a/crates/superposition_provider/src/data_source/http.rs b/crates/superposition_provider/src/data_source/http.rs index fa24a191c..310ceba3e 100644 --- a/crates/superposition_provider/src/data_source/http.rs +++ b/crates/superposition_provider/src/data_source/http.rs @@ -98,8 +98,11 @@ impl HttpDataSource { Ok(experiments_response) } +} - async fn fetch_config_with_filters( +#[async_trait] +impl SuperpositionDataSource for HttpDataSource { + async fn fetch_filtered_config( &self, context: Option>, prefix_filter: Option>, @@ -156,27 +159,6 @@ impl HttpDataSource { resp } -} - -#[async_trait] -impl SuperpositionDataSource for HttpDataSource { - async fn fetch_config( - &self, - if_modified_since: Option>, - ) -> Result> { - self.fetch_config_with_filters(None, None, if_modified_since) - .await - } - - async fn fetch_filtered_config( - &self, - context: Option>, - prefix_filter: Option>, - if_modified_since: Option>, - ) -> Result> { - self.fetch_config_with_filters(context, prefix_filter, if_modified_since) - .await - } async fn fetch_active_experiments( &self, diff --git a/crates/superposition_provider/src/local_provider.rs b/crates/superposition_provider/src/local_provider.rs index 4d0ff8896..cbba4c0d1 100644 --- a/crates/superposition_provider/src/local_provider.rs +++ b/crates/superposition_provider/src/local_provider.rs @@ -607,39 +607,32 @@ impl FeatureProvider for LocalResolutionProvider { #[async_trait] impl SuperpositionDataSource for LocalResolutionProvider { - async fn fetch_config( + async fn fetch_filtered_config( &self, + context: Option>, + prefix_filter: Option>, if_modified_since: Option>, ) -> Result> { if if_modified_since.is_some() { log::debug!("LocalResolutionProvider: ignoring if_modified_since for config, always returning cached data"); } - let cached = self.cached_config.read().await; - match cached.as_ref() { - Some(data) => Ok(FetchResponse::Data(data.clone())), - None => Err(SuperpositionError::ConfigError( - "No cached config available".into(), - )), - } - } - async fn fetch_filtered_config( - &self, - context: Option>, - prefix_filter: Option>, - if_modified_since: Option>, - ) -> Result> { - let resp = self - .fetch_config(if_modified_since) - .await? - .map_data(|mut c| { - let prefix = prefix_filter.map(HashSet::from_iter); - c.data = c.data.filter(context.as_ref(), prefix.as_ref()); + let mut config_data = { + let cached = self.cached_config.read().await; + match cached.as_ref() { + Some(data) => data.clone(), + None => { + return Err(SuperpositionError::ConfigError( + "No cached config available".into(), + )) + } + } + }; - c - }); + let prefix = prefix_filter.map(HashSet::from_iter); + config_data.data = config_data.data.filter(context.as_ref(), prefix.as_ref()); - Ok(resp) + Ok(FetchResponse::Data(config_data)) } async fn fetch_active_experiments(