Skip to content

allow for loading initial information of a TestData from a file #182

@jmhodges-color

Description

@jmhodges-color

Is your feature request related to a problem? Please describe.
We have very many feature flags in our codebase, many of which are permanent for partial availability reasons.

We'd like our devs' tests to be initialized with a known-good state of those feature flags that we'd store on disk and then use the TestData API to override those values in their tests.

Describe the solution you'd like

It'd be really nice for Files and TestData to have a way to cross through them.

Something like:

feature_store: FeatureStore = Files.load_from_file('/path/to/flagdata.json'); 
td = TestData(feature_store)
td.update(td.flag(...))

Describe alternatives you've considered
Currently, we try to not know much of anything about the schema of the flag data on disk from a dump of https://app.launchdarkly.com/sdk/latest-all

We have a subclass of TestData that takes a FeatureStore argument, and overrides _make_init_data to use the FeatureStore's values as the initial values in _TestDataSource's FeatureStore (with some jiggering of version). That could probably be made into how TestData operates if created with a FeatureStore.

However, getting the FeatureStore to pass into TestData filled with data is grody because there's no public way to turn file data into a FeatureStore directly without knowing details about the schema of the flag data.

One horrible hack imagined was creating a Config with an InMemoryFeatureStore as feature_store and a Files.new_data_source() as update_processor_class, passing that Config to an LDClient, and waiting for the LDClient to initialize. Then, using the contents of InMemoryFeatureStore as the initial FeatureStore our subclass uses and throwing away that Files-y LDClient.

Something like:

feature_store = InMemoryFeatureStore()
data_source_factory = Files.new_data_source(paths=["flagdata.json"], auto_update=False)
files_ldclient = LDClient(Config(sdk_key='sdk-fake', update_processor_class=data_source_factory, send_events=False, feature_store=feature_store)
td = OurTestData(feature_store) # See "Additional context"

Additional context

The subclass we wrote:

class OurTestData(TestData):
     def __init__(self, initial_feature_store: Optional[FeatureStore]):
         self.initial_feature_store = initial_feature_store
         super().__init__()
 
     def _make_init_data(self) -> dict:
         features = {}
         if self.initial_feature_store:
           features = self.initial_feature_store.all(FEATURES, lambda x: x)
         for k, v in self._current_flags.items():
             initial_v = features.get(k, None)
             if initial_v:
               v['version'] = initial_v['version']
             features[k] = v
 
         return {FEATURES: features}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions