ruby-server-sdk icon indicating copy to clipboard operation
ruby-server-sdk copied to clipboard

feat: Configurably allow duplicates in FileDataSourceImpl

Open djmorton42 opened this issue 5 months ago • 1 comments

Related issues

N/A

Describe the solution you've provided

This PR modifies the FileDataSourceImpl to accept an additional value in the options hash called allow_duplicates. false will be used if no value is provided which will cause this to operate exactly as it does prior to this change.

Configuring allow_duplicates to be true will not raise an error when flag or segment values are loaded from multiple files and a subsequent file contains a key found in a previous file.

The purpose of this is to allow a notion of a 'local' override during development. A project may have a /config/feature_flags.yml and a developer adds a flag to it for a feature they are developing. If they want to commit code with this feature, but don't want other developers encountering it yet, they will have to remember to turn the feature off prior to commiting their change.

With this change, a project could have a /config/feature_flags.yml which contains the flag values other developers should see, and a /config/feature_flags.local.yml file, ignored by source control and not checked in. As a developer is working on the new feature, they can have it enabled in the .local file so they can see the feature and work with it, but disabled in the committed file so it does not impact other developers. As the files are processed in the order they are defined, the values defined in subsequent files would take precedence over duplicate values defined in prior files.

Requirements

  • [x] I have added test coverage for new or changed functionality
  • [x] I have followed the repository's pull request submission guidelines
  • [x] I have validated my changes against all supported platform versions

Describe alternatives you've considered

I considered trying to accomplish this via subclassing the FileDataSourceImpl but it is not really designed for extension based on the amount of private functionality and state that it maintains internally. As such, an upstream change to the implementation itself seemed like the path of least resistance.

Additional context

# Configuration:

options = {
  paths: ["config/feature_flags.yml", "config/feature_flags.local.yml"],
  auto_update: true,
  allow_duplicates: true,
}

data_source = ::LaunchDarkly::Integrations::FileData.data_source(options)

config = ::LaunchDarkly::Config.new(
  data_source: data_source,
  send_events: false,
)

ld_client = ::LaunchDarkly::LDClient.new(SDK_KEY, config)
# config/feature_flags.yml
flagValues:
  flag1: false
  flag2: true
  flag3: false
# config/feature_flags.local.yml
  flag1: true
# Usage

ld_client.variation("flag1", some_context, false) # returns `true` because it is overridden in .local.yml file
ld_client.variation("flag2", some_context, false) # returns `true`
ld_client.variation("flag3", some_context, false) # returns `false`

djmorton42 avatar Sep 18 '24 14:09 djmorton42