Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 23 additions & 5 deletions contract-tests/async_client_entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
)
from ldclient.impl.util import Result
from ldclient.integrations import Redis
from ldclient.integrations.overrides import FileOverrideSourceBuilder
from ldclient.interfaces import DataStoreMode
from ldclient.migrations import (
AsyncMigratorBuilder,
Expand Down Expand Up @@ -56,8 +57,10 @@ async def start(self):
}

datasystem_config = config_params.get('dataSystem')
if config_params.get('overrides') is not None and datasystem_config is None:
raise ValueError("flag overrides require the data system to be configured")
if datasystem_config is not None:
opts["datasystem_config"] = _build_async_data_system(datasystem_config, opts)
opts["datasystem_config"] = _build_async_data_system(datasystem_config, opts, config_params.get('overrides'))
elif config_params.get("streaming") is not None:
streaming = config_params["streaming"]
if streaming.get("baseUri") is not None:
Expand Down Expand Up @@ -297,13 +300,13 @@ def _set_optional_value(params_in: dict, name_in: str, func: Callable[[Any], Any
func(params_in[name_in])


def _build_async_data_system(datasystem_config: dict, opts: dict) -> AsyncDataSystemConfig:
def _build_async_data_system(datasystem_config: dict, opts: dict, overrides_config: Optional[dict] = None) -> AsyncDataSystemConfig:
"""Build an AsyncDataSystemConfig from the harness's dataSystem config.

Wires the FDv2 initializers, the ordered synchronizer chain, the FDv1
fallback synchronizer, the payload filter, and an optional async
persistent store. The async client injects its shared aiohttp session
into these builders when it starts.
fallback synchronizer, the payload filter, an optional async persistent
store, and the optional file-based override source. The async client
injects its shared aiohttp session into these builders when it starts.
"""
initializers: Optional[list] = None
init_configs = datasystem_config.get('initializers')
Expand Down Expand Up @@ -356,6 +359,9 @@ def _build_async_data_system(datasystem_config: dict, opts: dict) -> AsyncDataSy
"fdv1_fallback_synchronizer": fdv1_fallback_synchronizer,
}

if overrides_config is not None:
ds_kwargs["override_source"] = _make_override_source(overrides_config)

store_config = datasystem_config.get("store")
if store_config is not None:
persistent_store_config = store_config.get("persistentDataStore")
Expand All @@ -370,6 +376,18 @@ def _build_async_data_system(datasystem_config: dict, opts: dict) -> AsyncDataSy
return AsyncDataSystemConfig(**ds_kwargs)


def _make_override_source(params: dict) -> FileOverrideSourceBuilder:
"""
Builds the file-based override source from the harness's overrides configuration. The
files are written by the harness on a file system shared with this service.
"""
builder = FileOverrideSourceBuilder(params["filePaths"])
_set_optional_value(params, "duplicateKeysHandling", builder.duplicate_keys_handling)
_set_optional_value(params, "changeDetection", builder.change_detection)
_set_optional_time(params, "pollIntervalMs", builder.poll_interval)
return builder


def _create_async_persistent_store(persistent_store_config: dict):
"""Create an async persistent feature store from the harness config.

Expand Down
1 change: 1 addition & 0 deletions contract-tests/async_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ async def handle_status(request: aiohttp.web.Request) -> aiohttp.web.Response:
'migrations',
'persistent-data-store-redis',
'fdv1-fallback',
'flag-overrides',
]
}
return aiohttp.web.Response(
Expand Down
19 changes: 19 additions & 0 deletions contract-tests/client_entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from ldclient.feature_store import CacheConfig
from ldclient.impl.datasourcev2.polling import PollingDataSourceBuilder
from ldclient.integrations import Consul, DynamoDB, Redis
from ldclient.integrations.overrides import FileOverrideSourceBuilder
from ldclient.interfaces import DataStoreMode


Expand All @@ -44,6 +45,8 @@ def __init__(self, tag, config):
}

datasystem_config = config.get('dataSystem')
if config.get('overrides') is not None and datasystem_config is None:
raise ValueError("flag overrides require the data system to be configured")
if datasystem_config is not None:
datasystem = custom()

Expand Down Expand Up @@ -107,6 +110,10 @@ def __init__(self, tag, config):

datasystem.data_store(store, store_mode)

overrides_config = config.get('overrides')
if overrides_config is not None:
datasystem.overrides(_make_override_source(overrides_config))

opts["datasystem_config"] = datasystem.build()

elif config.get("streaming") is not None:
Expand Down Expand Up @@ -325,6 +332,18 @@ def _set_optional_value(params_in: dict, name_in: str, func: Callable[[Any], Any
func(params_in[name_in])


def _make_override_source(params: dict) -> FileOverrideSourceBuilder:
"""
Builds the file-based override source from the harness's overrides configuration. The
files are written by the harness on a file system shared with this service.
"""
builder = FileOverrideSourceBuilder(params["filePaths"])
_set_optional_value(params, "duplicateKeysHandling", builder.duplicate_keys_handling)
_set_optional_value(params, "changeDetection", builder.change_detection)
_set_optional_time(params, "pollIntervalMs", builder.poll_interval)
return builder


def _create_persistent_store(persistent_store_config: dict):
"""
Creates a persistent store instance based on the configuration.
Expand Down
1 change: 1 addition & 0 deletions contract-tests/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ def status():
'flag-change-listeners',
'flag-value-change-listeners',
'fdv1-fallback',
'flag-overrides',
]
}
return json.dumps(body), 200, {'Content-type': 'application/json'}
Expand Down
Loading