Class: LaunchDarkly::Integrations::TestDataV2

Inherits:
Object
  • Object
show all
Defined in:
lib/ldclient-rb/integrations/test_data_v2.rb,
lib/ldclient-rb/integrations/test_data_v2/flag_builder_v2.rb

Overview

A mechanism for providing dynamically updatable feature flag state in a simplified form to an SDK client in test scenarios using the FDv2 protocol.

This type is not stable, and not subject to any backwards compatibility guarantees or semantic versioning. It is not suitable for production usage.

Do not use it. You have been warned.

Unlike FileData, this mechanism does not use any external resources. It provides only the data that the application has put into it using the #update method.

The above example uses a simple boolean flag, but more complex configurations are possible using the methods of the FlagBuilderV2 that is returned by #flag. FlagBuilderV2 supports many of the ways a flag can be configured on the LaunchDarkly dashboard, but does not currently support 1. rule operators other than “in” and “not in”, or 2. percentage rollouts.

If the same TestDataV2 instance is used to configure multiple LDClient instances, any changes made to the data will propagate to all of the LDClient instances.

Examples:

require 'ldclient-rb/integrations/test_data_v2'

td = LaunchDarkly::Integrations::TestDataV2.data_source
td.update(td.flag('flag-key-1').variation_for_all(true))

# Configure the data system with TestDataV2 as both initializer and synchronizer
# Note: This example assumes FDv2 data system configuration is available
# data_config = LaunchDarkly::Impl::DataSystem::Config.custom
# data_config.initializers([td.method(:build_initializer)])
# data_config.synchronizers(td.method(:build_synchronizer))

# config = LaunchDarkly::Config.new(
#   sdk_key,
#   datasystem_config: data_config.build
# )

# flags can be updated at any time:
td.update(td.flag('flag-key-1')
    .variation_for_user('some-user-key', true)
    .fallthrough_variation(false))

Defined Under Namespace

Classes: FlagBuilderV2, FlagRuleBuilderV2

Constant Summary collapse

TRUE_VARIATION_INDEX =

Constants for boolean flag variation indices

0
FALSE_VARIATION_INDEX =
1

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeTestDataV2

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of TestDataV2.



60
61
62
63
64
65
66
67
# File 'lib/ldclient-rb/integrations/test_data_v2.rb', line 60

def initialize
  @flag_builders = Hash.new
  @current_flags = Hash.new
  @current_segments = Hash.new
  @lock = Concurrent::ReadWriteLock.new
  @instances = Array.new
  @version = 0
end

Class Method Details

.data_sourceTestDataV2

Creates a new instance of the test data source.

Returns:

  • (TestDataV2)

    a new configurable test data source



55
56
57
# File 'lib/ldclient-rb/integrations/test_data_v2.rb', line 55

def self.data_source
  self.new
end

.variation_for_boolean(variation) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



13
14
15
# File 'lib/ldclient-rb/integrations/test_data_v2/flag_builder_v2.rb', line 13

def self.variation_for_boolean(variation)
  variation ? TRUE_VARIATION_INDEX : FALSE_VARIATION_INDEX
end

Instance Method Details

#add_instance(instance) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:



168
169
170
171
172
# File 'lib/ldclient-rb/integrations/test_data_v2.rb', line 168

def add_instance(instance)
  @lock.with_write_lock do
    @instances.push(instance)
  end
end

#build_initializer(sdk_key, config) ⇒ LaunchDarkly::Impl::Integrations::TestData::TestDataSourceV2

Creates an initializer that can be used with the FDv2 data system.

Parameters:

Returns:



231
232
233
# File 'lib/ldclient-rb/integrations/test_data_v2.rb', line 231

def build_initializer(sdk_key, config)
  LaunchDarkly::Impl::Integrations::TestData::TestDataSourceV2.new(self)
end

#build_synchronizer(sdk_key, config) ⇒ LaunchDarkly::Impl::Integrations::TestData::TestDataSourceV2

Creates a synchronizer that can be used with the FDv2 data system.

Parameters:

Returns:



242
243
244
# File 'lib/ldclient-rb/integrations/test_data_v2.rb', line 242

def build_synchronizer(sdk_key, config)
  LaunchDarkly::Impl::Integrations::TestData::TestDataSourceV2.new(self)
end

#closed_instance(instance) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:



160
161
162
163
164
# File 'lib/ldclient-rb/integrations/test_data_v2.rb', line 160

def closed_instance(instance)
  @lock.with_write_lock do
    @instances.delete(instance) if @instances.include?(instance)
  end
end

#flag(key) ⇒ FlagBuilderV2

Creates or copies a FlagBuilderV2 for building a test flag configuration.

If this flag key has already been defined in this TestDataV2 instance, then the builder starts with the same configuration that was last provided for this flag.

Otherwise, it starts with a new default configuration in which the flag has true and false variations, is true for all contexts when targeting is turned on and false otherwise, and currently has targeting turned on. You can change any of those properties, and provide more complex behavior, using the FlagBuilderV2 methods.

Once you have set the desired configuration, pass the builder to #update.

Parameters:

  • key (String)

    the flag key

Returns:



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/ldclient-rb/integrations/test_data_v2.rb', line 85

def flag(key)
  existing_builder = @lock.with_read_lock do
    if @flag_builders.key?(key) && !@flag_builders[key].nil?
      @flag_builders[key]
    else
      nil
    end
  end

  if existing_builder.nil?
    LaunchDarkly::Integrations::TestDataV2::FlagBuilderV2.new(key).boolean_flag
  else
    existing_builder.clone
  end
end

#get_versionObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



150
151
152
153
154
155
156
# File 'lib/ldclient-rb/integrations/test_data_v2.rb', line 150

def get_version
  @lock.with_write_lock do
    version = @version
    @version += 1
    version
  end
end

#make_init_dataObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



140
141
142
143
144
145
146
147
# File 'lib/ldclient-rb/integrations/test_data_v2.rb', line 140

def make_init_data
  @lock.with_read_lock do
    {
      flags: @current_flags.dup,
      segments: @current_segments.dup,
    }
  end
end

#update(flag_builder) ⇒ TestDataV2

Updates the test data with the specified flag configuration.

This has the same effect as if a flag were added or modified on the LaunchDarkly dashboard. It immediately propagates the flag change to any LDClient instance(s) that you have already configured to use this TestDataV2. If no LDClient has been started yet, it simply adds this flag to the test data which will be provided to any LDClient that you subsequently configure.

Any subsequent changes to this FlagBuilderV2 instance do not affect the test data, unless you call #update again.

Parameters:

Returns:



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/ldclient-rb/integrations/test_data_v2.rb', line 116

def update(flag_builder)
  instances_copy = []
  new_flag = nil
  @lock.with_write_lock do
    old_flag = @current_flags[flag_builder._key]
    old_version = old_flag ? old_flag[:version] : 0

    new_flag = flag_builder.build(old_version + 1)

    @current_flags[flag_builder._key] = new_flag
    @flag_builders[flag_builder._key] = flag_builder.clone

    # Create a copy of instances while holding the lock to avoid race conditions
    instances_copy = @instances.dup
  end

  instances_copy.each do |instance|
    instance.upsert_flag(new_flag)
  end

  self
end

#use_preconfigured_segment(segment) ⇒ TestDataV2

Copies a full segment data model object into the test data.

It immediately propagates the change to any LDClient instance(s) that you have already configured to use this TestDataV2. If no LDClient has been started yet, it simply adds this segment to the test data which will be provided to any LDClient that you subsequently configure.

This method is currently the only way to inject segment data, since there is no builder API for segments. It is mainly intended for the SDK’s own tests of segment functionality, since application tests that need to produce a desired evaluation state could do so more easily by just setting flag values.

Parameters:

Returns:



191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/ldclient-rb/integrations/test_data_v2.rb', line 191

def use_preconfigured_segment(segment)
  instances_copy = []
  segment_key = nil
  updated_segment = nil

  @lock.with_write_lock do
    # Convert to hash and normalize keys to symbols
    segment_hash = if segment.is_a?(Hash)
      segment.transform_keys(&:to_sym)
    else
      segment.as_json
    end
    segment_key = segment_hash[:key]

    old_segment = @current_segments[segment_key]
    old_version = old_segment ? old_segment[:version] : 0

    updated_segment = segment_hash.dup
    updated_segment[:version] = old_version + 1

    @current_segments[segment_key] = updated_segment

    # Create a copy of instances while holding the lock to avoid race conditions
    instances_copy = @instances.dup
  end

  instances_copy.each do |instance|
    instance.upsert_segment(updated_segment)
  end

  self
end