Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: mock client for local dev and test ENVs #93

Closed
davydovanton opened this issue Feb 8, 2018 · 11 comments
Closed

Proposal: mock client for local dev and test ENVs #93

davydovanton opened this issue Feb 8, 2018 · 11 comments

Comments

@davydovanton
Copy link

Hey,

We use dry-system for booting dependencies to our project and that's why I think it will be cool to create mock client only for local usage.

Of course, we can mock LDClient in spec helper, but we want to have aт ability to develop without an internet connection too (or with a real client if we need to check something). I hope that it will be better to use the native client, something like this:

# booting app process

if container.settings.launch_darkly_sdk_key
  client = LaunchDarkly::LDClient.new(container.settings.launch_darkly_sdk_key)
else
  client = LaunchDarkly::MockClient.new
end

WDYT? I can make PR for this 👍

@ashanbrown
Copy link
Contributor

Thanks for the note @davydovanton. Could you give a few more details about what the behavior of your suggested MockClient? Would this be for local development or is it for testing? Also, given that you can inject the dependencies with dry-system, can you just inject a mock of your choice for either development or test? I'm concerned that it might be hard to come up with an interface to drive MockClient that would satisfy all users. If you know what the interface might look like, we'd be happy to give it some consideration.

@eli-darkly
Copy link
Contributor

An alternate approach, which we have already started implementing in some of the SDKs but haven't released yet, is to provide a pluggable component to replace only the streaming/polling logic in the existing client, which would provide feature flag data by some other mechanism such as reading it from a file. A file-based data source, combined with the existing ability to turn off event posting, that would provide a usable offline client without having to reimplement other parts of the client API.

@rodrigodiez
Copy link

@eli-darkly that sounds interesting. Is it still unreleased? We desperately need to find a valid approach for local BDD scenarios without requiring a connection to LD in our PHP applications. Ie

Given the feature flag 'new-section' is 'true' for the user 'chuck.norris@gmail.com'
When I load the homepage
Then I should see the 'New section'

Without LD sdk support for loading flags from a source with a public specification we are quite limited

@davydovanton
Copy link
Author

@ashanbrown sorry for delay. Of course. I will be happy to use mock (local?) LD client for unit testing our system and also for local development in cases when an internet connection is failed (in aircraft or train for example). Also, it will be useful for our team because we can downgrade complexity of starting develop for a new developers (you don't need to think about api keys and secrets).

About dry system. I have access for booting my dependencies. It's mean that for testing and development I can boot mock client and use it as a regular client (which we use in production). Also, for testing we can inject (with dependency injection) any object with similar interface as a client.

About interface. We can start use most popular methods, get feedback and drop this mock or add more mocked methods. WDYT?


@eli-darkly so, my general rule - allow to use project with LD client without internet connection (for development). I'll be happy to use regular LD client but without any network calls. That's why your approach looks awesome for me too 👍

Also, I'll be happy if you share some code examples of this idea

@eli-darkly
Copy link
Contributor

Unfortunately, the file-based data source I mentioned isn't available yet. It's been on our backlog for a while.

@eli-darkly
Copy link
Contributor

eli-darkly commented Oct 5, 2018

However, if anyone is interested in trying to implement something similar themselves, here is the simplest way to do it:

The client keeps the current state of feature flags in an object called the feature store. In normal usage, there's another component that communicates with LaunchDarkly, receives new versions of the flags when they're updated, and puts those into the feature store. However, you can also update the store directly.

First, you would create your own feature store instance:

  store = LaunchDarkly::InMemoryFeatureStore.new()

Now, you need to put some flags in it. I'll get to what the structure of the flags is in a moment, but here is how you would store them if you had two flags:

  data = {
    LaunchDarkly::FEATURES => {
      flag1Key: flag1Data,
      flag2Key: flag2Data
    }
  }
  store.init(features)

Then, to make your client use this flag data, and not communicate with LaunchDarkly at all, you would configure it like this:

  config = LaunchDarkly::Config.new({
    offline: true,
    feature_store: store
  })
  client = LaunchDarkly::LDClient.new(mySdkKey, config)

Now, what does the data actually look like for a flag? Unfortunately the data model is a bit complicated, and not documented since normally the SDK takes care of all the details. In our proposed file-based fixture, there would be an option to simplify this so you could just say "the flag should have value X". But in the meantime, here are two ways you can construct a flag directly:

  1. Create the flag in LaunchDarkly, give it the exact properties you want, and then query the JSON data for that flag using the following HTTP request:
curl -H 'Authorization:mySdkKey' https://app.launchdarkly.com/sdk/latest-flags/myFlagKey
  1. If you want to just construct a flag that always returns the same value for every user, here is the simplest JSON representation of it:
{
  "key": "myFlagKey",
  "offVariation": 0,
  "variations": [ <put your desired JSON value here - note the square brackets> ]
}

I hope this is helpful.

@eli-darkly
Copy link
Contributor

eli-darkly commented Nov 3, 2018

@davydovanton @rodrigodiez - Sorry for the delay, but we have finally released v5.4.0 of the SDK which includes the ability to use fake flag data from a file. Here's an example of how to do this.

First, in your project directory, create a file called (for example) test-flags.json, with the following format:

{
  "flagValues": {
    "flagKey1": "value1",
    "flagKey2": "value2"
  }
}

(In place of flagKeyN and valueN, list whatever flag keys and values you want. The values can be of any JSON type.)

Then, when you create a LaunchDarkly client in your tests, configure it like this:

    file_data = LaunchDarkly::FileDataSource(paths: [ "./test-flags.json" ])
    config = LaunchDarkly::Config.new(update_processor_factory: file_data, send_events: false)
    client = LaunchDarkly::Client.new(sdk_key, config)

Specifying update_processor_factory: file_data causes the client to use the file data instead of requesting flags from LaunchDarkly, and send_events: false ensures that it will not try to send any analytics events to LaunchDarkly, so there will be no LaunchDarkly HTTP traffic at all.

You can also use a lengthier file format if you need to specify more complicated flag behavior (rules or targets) instead of just simple values. Full documentation is here.

@davydovanton
Copy link
Author

awesome, thanks!

@rodrigodiez
Copy link

@eli-darkly thanks for the update and the effort. This looks quite promising and will make our lives much easier!

Is this already implemented or in the roadmap for clients in other languages? Specifically nodejs and php?

@eli-darkly
Copy link
Contributor

@rodrigodiez It's been implemented in most of the other SDKs, but unfortunately Node and PHP are the two that we haven't done yet. Shouldn't take too much longer though.

Also, here is the high-level overview of the feature in the online docs.

@eli-darkly
Copy link
Contributor

@rodrigodiez We've added this for Node.js in today's release, and the page I linked to in my last comment now includes example code for Node. The PHP version is still in progress.

eli-darkly added a commit that referenced this issue Jan 17, 2019
implement dependency ordering for feature store data
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants