launchdarkly-server-sdk 8.10.2 → 8.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,162 @@
1
+ module LaunchDarkly
2
+ module Interfaces
3
+ #
4
+ # Mixin that defines the required methods of a feature store implementation. The LaunchDarkly
5
+ # client uses the feature store to persist feature flags and related objects received from
6
+ # the LaunchDarkly service. Implementations must support concurrent access and updates.
7
+ # For more about how feature stores can be used, see:
8
+ # [Using a persistent feature store](https://docs.launchdarkly.com/sdk/features/storing-data#ruby).
9
+ #
10
+ # An entity that can be stored in a feature store is a hash that can be converted to and from
11
+ # JSON, and that has at a minimum the following properties: `:key`, a string that is unique
12
+ # among entities of the same kind; `:version`, an integer that is higher for newer data;
13
+ # `:deleted`, a boolean (optional, defaults to false) that if true means this is a
14
+ # placeholder for a deleted entity.
15
+ #
16
+ # To represent the different kinds of objects that can be stored, such as feature flags and
17
+ # segments, the SDK will provide a "kind" object; this is a hash with a single property,
18
+ # `:namespace`, which is a short string unique to that kind. This string can be used as a
19
+ # collection name or a key prefix.
20
+ #
21
+ # The default implementation is {LaunchDarkly::InMemoryFeatureStore}. Several implementations
22
+ # that use databases can be found in {LaunchDarkly::Integrations}. If you want to write a new
23
+ # implementation, see {LaunchDarkly::Integrations::Util} for tools that can make this task
24
+ # simpler.
25
+ #
26
+ module FeatureStore
27
+ #
28
+ # Initializes (or re-initializes) the store with the specified set of entities. Any
29
+ # existing entries will be removed. Implementations can assume that this data set is up to
30
+ # date-- there is no need to perform individual version comparisons between the existing
31
+ # objects and the supplied features.
32
+ #
33
+ # If possible, the store should update the entire data set atomically. If that is not possible,
34
+ # it should iterate through the outer hash and then the inner hash using the existing iteration
35
+ # order of those hashes (the SDK will ensure that the items were inserted into the hashes in
36
+ # the correct order), storing each item, and then delete any leftover items at the very end.
37
+ #
38
+ # @param all_data [Hash] a hash where each key is one of the data kind objects, and each
39
+ # value is in turn a hash of string keys to entities
40
+ # @return [void]
41
+ #
42
+ def init(all_data)
43
+ end
44
+
45
+ #
46
+ # Returns the entity to which the specified key is mapped, if any.
47
+ #
48
+ # @param kind [Object] the kind of entity to get
49
+ # @param key [String] the unique key of the entity to get
50
+ # @return [Hash] the entity; nil if the key was not found, or if the stored entity's
51
+ # `:deleted` property was true
52
+ #
53
+ def get(kind, key)
54
+ end
55
+
56
+ #
57
+ # Returns all stored entities of the specified kind, not including deleted entities.
58
+ #
59
+ # @param kind [Object] the kind of entity to get
60
+ # @return [Hash] a hash where each key is the entity's `:key` property and each value
61
+ # is the entity
62
+ #
63
+ def all(kind)
64
+ end
65
+
66
+ #
67
+ # Attempt to add an entity, or update an existing entity with the same key. An update
68
+ # should only succeed if the new item's `:version` is greater than the old one;
69
+ # otherwise, the method should do nothing.
70
+ #
71
+ # @param kind [Object] the kind of entity to add or update
72
+ # @param item [Hash] the entity to add or update
73
+ # @return [void]
74
+ #
75
+ def upsert(kind, item)
76
+ end
77
+
78
+ #
79
+ # Attempt to delete an entity if it exists. Deletion should only succeed if the
80
+ # `version` parameter is greater than the existing entity's `:version`; otherwise, the
81
+ # method should do nothing.
82
+ #
83
+ # @param kind [Object] the kind of entity to delete
84
+ # @param key [String] the unique key of the entity
85
+ # @param version [Integer] the entity must have a lower version than this to be deleted
86
+ # @return [void]
87
+ #
88
+ def delete(kind, key, version)
89
+ end
90
+
91
+ #
92
+ # Checks whether this store has been initialized. That means that `init` has been called
93
+ # either by this process, or (if the store can be shared) by another process. This
94
+ # method will be called frequently, so it should be efficient. You can assume that if it
95
+ # has returned true once, it can continue to return true, i.e. a store cannot become
96
+ # uninitialized again.
97
+ #
98
+ # @return [Boolean] true if the store is in an initialized state
99
+ #
100
+ def initialized?
101
+ end
102
+
103
+ #
104
+ # Performs any necessary cleanup to shut down the store when the client is being shut down.
105
+ #
106
+ # @return [void]
107
+ #
108
+ def stop
109
+ end
110
+
111
+ #
112
+ # WARN: This isn't a required method on a FeatureStore yet. The SDK will
113
+ # currently check if the provided store responds to this method, and if
114
+ # it does, will take appropriate action based on the documented behavior
115
+ # below. This will become required in a future major version release of
116
+ # the SDK.
117
+ #
118
+ # Returns true if this data store implementation supports status
119
+ # monitoring.
120
+ #
121
+ # This is normally only true for persistent data stores but it could also
122
+ # be true for any custom {FeatureStore} implementation.
123
+ #
124
+ # Returning true means that the store guarantees that if it ever enters
125
+ # an invalid state (that is, an operation has failed or it knows that
126
+ # operations cannot succeed at the moment), it will publish a status
127
+ # update, and will then publish another status update once it has
128
+ # returned to a valid state.
129
+ #
130
+ # Custom implementations must implement `def available?` which
131
+ # synchronously checks if the store is available. Without this method,
132
+ # the SDK cannot ensure status updates will occur once the store has gone
133
+ # offline.
134
+ #
135
+ # The same value will be returned from
136
+ # {StatusProvider::monitoring_enabled?}.
137
+ #
138
+ # def monitoring_enabled? end
139
+
140
+ #
141
+ # WARN: This isn't a required method on a FeatureStore. The SDK will
142
+ # check if the provided store responds to this method, and if it does,
143
+ # will take appropriate action based on the documented behavior below.
144
+ # Usage of this method will be dropped in a future version of the SDK.
145
+ #
146
+ # Tests whether the data store seems to be functioning normally.
147
+ #
148
+ # This should not be a detailed test of different kinds of operations,
149
+ # but just the smallest possible operation to determine whether (for
150
+ # instance) we can reach the database.
151
+ #
152
+ # Whenever one of the store's other methods throws an exception, the SDK
153
+ # will assume that it may have become unavailable (e.g. the database
154
+ # connection was lost). The SDK will then call {#available?} at intervals
155
+ # until it returns true.
156
+ #
157
+ # @return [Boolean] true if the underlying data store is reachable
158
+ #
159
+ # def available? end
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,106 @@
1
+ module LaunchDarkly
2
+ module Interfaces
3
+ #
4
+ # An interface for tracking changes in feature flag configurations.
5
+ #
6
+ # An implementation of this interface is returned by {LaunchDarkly::LDClient#flag_tracker}.
7
+ # Application code never needs to implement this interface.
8
+ #
9
+ module FlagTracker
10
+ #
11
+ # Registers a listener to be notified of feature flag changes in general.
12
+ #
13
+ # The listener will be notified whenever the SDK receives any change to any feature flag's configuration,
14
+ # or to a user segment that is referenced by a feature flag. If the updated flag is used as a prerequisite
15
+ # for other flags, the SDK assumes that those flags may now behave differently and sends flag change events
16
+ # for them as well.
17
+ #
18
+ # Note that this does not necessarily mean the flag's value has changed for any particular evaluation
19
+ # context, only that some part of the flag configuration was changed so that it may return a
20
+ # different value than it previously returned for some context. If you want to track flag value changes,
21
+ # use {#add_flag_value_change_listener} instead.
22
+ #
23
+ # It is possible, given current design restrictions, that a listener might be notified when no change has
24
+ # occurred. This edge case will be addressed in a later version of the SDK. It is important to note this issue
25
+ # does not affect {#add_flag_value_change_listener} listeners.
26
+ #
27
+ # If using the file data source, any change in a data file will be treated as a change to every flag. Again,
28
+ # use {#add_flag_value_change_listener} (or just re-evaluate the flag # yourself) if you want to know whether
29
+ # this is a change that really affects a flag's value.
30
+ #
31
+ # Change events only work if the SDK is actually connecting to LaunchDarkly (or using the file data source).
32
+ # If the SDK is only reading flags from a database then it cannot know when there is a change, because
33
+ # flags are read on an as-needed basis.
34
+ #
35
+ # The listener will be called from a worker thread.
36
+ #
37
+ # Calling this method for an already-registered listener has no effect.
38
+ #
39
+ # @param listener [#update]
40
+ #
41
+ def add_listener(listener) end
42
+
43
+ #
44
+ # Unregisters a listener so that it will no longer be notified of feature flag changes.
45
+ #
46
+ # Calling this method for a listener that was not previously registered has no effect.
47
+ #
48
+ # @param listener [Object]
49
+ #
50
+ def remove_listener(listener) end
51
+
52
+ #
53
+ # Registers a listener to be notified of a change in a specific feature flag's value for a specific
54
+ # evaluation context.
55
+ #
56
+ # When you call this method, it first immediately evaluates the feature flag. It then uses
57
+ # {#add_listener} to start listening for feature flag configuration
58
+ # changes, and whenever the specified feature flag changes, it re-evaluates the flag for the same context.
59
+ # It then calls your listener if and only if the resulting value has changed.
60
+ #
61
+ # All feature flag evaluations require an instance of {LaunchDarkly::LDContext}. If the feature flag you are
62
+ # tracking does not have any context targeting rules, you must still pass a dummy context such as
63
+ # `LDContext.with_key("for-global-flags")`. If you do not want the user to appear on your dashboard,
64
+ # use the anonymous property: `LDContext.create({key: "for-global-flags", kind: "user", anonymous: true})`.
65
+ #
66
+ # The returned listener represents the subscription that was created by this method
67
+ # call; to unsubscribe, pass that object (not your listener) to {#remove_listener}.
68
+ #
69
+ # @param key [Symbol]
70
+ # @param context [LaunchDarkly::LDContext]
71
+ # @param listener [#update]
72
+ #
73
+ def add_flag_value_change_listener(key, context, listener) end
74
+ end
75
+
76
+ #
77
+ # Change event fired when some aspect of the flag referenced by the key has changed.
78
+ #
79
+ class FlagChange
80
+ attr_accessor :key
81
+
82
+ # @param [Symbol] key
83
+ def initialize(key)
84
+ @key = key
85
+ end
86
+ end
87
+
88
+ #
89
+ # Change event fired when the evaluated value for the specified flag key has changed.
90
+ #
91
+ class FlagValueChange
92
+ attr_accessor :key
93
+ attr_accessor :old_value
94
+ attr_accessor :new_value
95
+
96
+ # @param [Symbol] key
97
+ # @param [Object] old_value
98
+ # @param [Object] new_value
99
+ def initialize(key, old_value, new_value)
100
+ @key = key
101
+ @old_value = old_value
102
+ @new_value = new_value
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,88 @@
1
+ module LaunchDarkly
2
+ module Interfaces
3
+ module Hooks
4
+ #
5
+ # Mixin for extending SDK functionality via hooks.
6
+ #
7
+ # All provided hook implementations **MUST** include this mixin. Hooks without this mixin will be ignored.
8
+ #
9
+ # This mixin includes default implementations for all hook handlers. This allows LaunchDarkly to expand the list
10
+ # of hook handlers without breaking customer integrations.
11
+ #
12
+ module Hook
13
+ #
14
+ # Get metadata about the hook implementation.
15
+ #
16
+ # @return [Metadata]
17
+ #
18
+ def metadata
19
+ Metadata.new('UNDEFINED')
20
+ end
21
+
22
+ #
23
+ # The before method is called during the execution of a variation method before the flag value has been
24
+ # determined. The method is executed synchronously.
25
+ #
26
+ # @param evaluation_series_context [EvaluationSeriesContext] Contains information about the evaluation being
27
+ # performed. This is not mutable.
28
+ # @param data [Hash] A record associated with each stage of hook invocations. Each stage is called with the data
29
+ # of the previous stage for a series. The input record should not be modified.
30
+ # @return [Hash] Data to use when executing the next state of the hook in the evaluation series.
31
+ #
32
+ def before_evaluation(evaluation_series_context, data)
33
+ data
34
+ end
35
+
36
+ #
37
+ # The after method is called during the execution of the variation method after the flag value has been
38
+ # determined. The method is executed synchronously.
39
+ #
40
+ # @param evaluation_series_context [EvaluationSeriesContext] Contains read-only information about the evaluation
41
+ # being performed.
42
+ # @param data [Hash] A record associated with each stage of hook invocations. Each stage is called with the data
43
+ # of the previous stage for a series.
44
+ # @param detail [LaunchDarkly::EvaluationDetail] The result of the evaluation. This value should not be
45
+ # modified.
46
+ # @return [Hash] Data to use when executing the next state of the hook in the evaluation series.
47
+ #
48
+ def after_evaluation(evaluation_series_context, data, detail)
49
+ data
50
+ end
51
+ end
52
+
53
+ #
54
+ # Metadata data class used for annotating hook implementations.
55
+ #
56
+ class Metadata
57
+ attr_reader :name
58
+
59
+ def initialize(name)
60
+ @name = name
61
+ end
62
+ end
63
+
64
+ #
65
+ # Contextual information that will be provided to handlers during evaluation series.
66
+ #
67
+ class EvaluationSeriesContext
68
+ attr_reader :key
69
+ attr_reader :context
70
+ attr_reader :default_value
71
+ attr_reader :method
72
+
73
+ #
74
+ # @param key [String]
75
+ # @param context [LaunchDarkly::LDContext]
76
+ # @param default_value [any]
77
+ # @param method [Symbol]
78
+ #
79
+ def initialize(key, context, default_value, method)
80
+ @key = key
81
+ @context = context
82
+ @default_value = default_value
83
+ @method = method
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,100 @@
1
+ module LaunchDarkly
2
+ module Interfaces
3
+ #
4
+ # Namespace for feature-flag based technology migration support.
5
+ #
6
+ module Migrations
7
+ #
8
+ # A migrator is the interface through which migration support is executed. A migrator is configured through the
9
+ # {LaunchDarkly::Migrations::MigratorBuilder} class.
10
+ #
11
+ module Migrator
12
+ #
13
+ # Uses the provided flag key and context to execute a migration-backed read operation.
14
+ #
15
+ # @param key [String]
16
+ # @param context [LaunchDarkly::LDContext]
17
+ # @param default_stage [Symbol]
18
+ # @param payload [Object, nil]
19
+ #
20
+ # @return [LaunchDarkly::Migrations::OperationResult]
21
+ #
22
+ def read(key, context, default_stage, payload = nil) end
23
+
24
+ #
25
+ # Uses the provided flag key and context to execute a migration-backed write operation.
26
+ #
27
+ # @param key [String]
28
+ # @param context [LaunchDarkly::LDContext]
29
+ # @param default_stage [Symbol]
30
+ # @param payload [Object, nil]
31
+ #
32
+ # @return [LaunchDarkly::Migrations::WriteResult]
33
+ #
34
+ def write(key, context, default_stage, payload = nil) end
35
+ end
36
+
37
+ #
38
+ # An OpTracker is responsible for managing the collection of measurements that which a user might wish to record
39
+ # throughout a migration-assisted operation.
40
+ #
41
+ # Example measurements include latency, errors, and consistency.
42
+ #
43
+ # This data can be provided to the {LaunchDarkly::LDClient.track_migration_op} method to relay this metric
44
+ # information upstream to LaunchDarkly services.
45
+ #
46
+ module OpTracker
47
+ #
48
+ # Sets the migration related operation associated with these tracking measurements.
49
+ #
50
+ # @param [Symbol] op The read or write operation symbol.
51
+ #
52
+ def operation(op) end
53
+
54
+ #
55
+ # Allows recording which origins were called during a migration.
56
+ #
57
+ # @param [Symbol] origin Designation for the old or new origin.
58
+ #
59
+ def invoked(origin) end
60
+
61
+ #
62
+ # Allows recording the results of a consistency check.
63
+ #
64
+ # This method accepts a callable which should take no parameters and return a single boolean to represent the
65
+ # consistency check results for a read operation.
66
+ #
67
+ # A callable is provided in case sampling rules do not require consistency checking to run. In this case, we can
68
+ # avoid the overhead of a function by not using the callable.
69
+ #
70
+ # @param [#call] is_consistent closure to return result of comparison check
71
+ #
72
+ def consistent(is_consistent) end
73
+
74
+ #
75
+ # Allows recording whether an error occurred during the operation.
76
+ #
77
+ # @param [Symbol] origin Designation for the old or new origin.
78
+ #
79
+ def error(origin) end
80
+
81
+ #
82
+ # Allows tracking the recorded latency for an individual operation.
83
+ #
84
+ # @param [Symbol] origin Designation for the old or new origin.
85
+ # @param [Float] duration Duration measurement in milliseconds (ms).
86
+ #
87
+ def latency(origin, duration) end
88
+
89
+ #
90
+ # Creates an instance of {LaunchDarkly::Impl::MigrationOpEventData}.
91
+ #
92
+ # @return [LaunchDarkly::Impl::MigrationOpEvent, String] A migration op event or a string describing the error.
93
+ # failure.
94
+ #
95
+ def build
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,136 @@
1
+ module LaunchDarkly
2
+ module Interfaces
3
+ module Plugins
4
+ #
5
+ # Metadata about the SDK.
6
+ #
7
+ class SdkMetadata
8
+ # The id of the SDK (e.g., "ruby-server-sdk")
9
+ # @return [String]
10
+ attr_reader :name
11
+
12
+ # The version of the SDK
13
+ # @return [String]
14
+ attr_reader :version
15
+
16
+ # The wrapper name if this SDK is a wrapper
17
+ # @return [String, nil]
18
+ attr_reader :wrapper_name
19
+
20
+ # The wrapper version if this SDK is a wrapper
21
+ # @return [String, nil]
22
+ attr_reader :wrapper_version
23
+
24
+ def initialize(name:, version:, wrapper_name: nil, wrapper_version: nil)
25
+ @name = name
26
+ @version = version
27
+ @wrapper_name = wrapper_name
28
+ @wrapper_version = wrapper_version
29
+ end
30
+ end
31
+
32
+ #
33
+ # Metadata about the application using the SDK.
34
+ #
35
+ class ApplicationMetadata
36
+ # The id of the application
37
+ # @return [String, nil]
38
+ attr_reader :id
39
+
40
+ # The version of the application
41
+ # @return [String, nil]
42
+ attr_reader :version
43
+
44
+ def initialize(id: nil, version: nil)
45
+ @id = id
46
+ @version = version
47
+ end
48
+ end
49
+
50
+ #
51
+ # Metadata about the environment in which the SDK is running.
52
+ #
53
+ class EnvironmentMetadata
54
+ # Information about the SDK
55
+ # @return [SdkMetadata]
56
+ attr_reader :sdk
57
+
58
+ # Information about the application
59
+ # @return [ApplicationMetadata, nil]
60
+ attr_reader :application
61
+
62
+ # The SDK key used to initialize the SDK
63
+ # @return [String, nil]
64
+ attr_reader :sdk_key
65
+
66
+ def initialize(sdk:, application: nil, sdk_key: nil)
67
+ @sdk = sdk
68
+ @application = application
69
+ @sdk_key = sdk_key
70
+ end
71
+ end
72
+
73
+ #
74
+ # Metadata about a plugin implementation.
75
+ #
76
+ class PluginMetadata
77
+ # A name representing the plugin instance
78
+ # @return [String]
79
+ attr_reader :name
80
+
81
+ def initialize(name)
82
+ @name = name
83
+ end
84
+ end
85
+
86
+ #
87
+ # Mixin for extending SDK functionality via plugins.
88
+ #
89
+ # All provided plugin implementations **MUST** include this mixin. Plugins without this mixin will be ignored.
90
+ #
91
+ # This mixin includes default implementations for optional methods. This allows LaunchDarkly to expand the list
92
+ # of plugin methods without breaking customer integrations.
93
+ #
94
+ # Plugins provide an interface which allows for initialization, access to credentials, and hook registration
95
+ # in a single interface.
96
+ #
97
+ module Plugin
98
+ #
99
+ # Get metadata about the plugin implementation.
100
+ #
101
+ # @return [PluginMetadata]
102
+ #
103
+ def metadata
104
+ PluginMetadata.new('UNDEFINED')
105
+ end
106
+
107
+ #
108
+ # Register the plugin with the SDK client.
109
+ #
110
+ # This method is called during SDK initialization to allow the plugin to set up any necessary integrations,
111
+ # register hooks, or perform other initialization tasks.
112
+ #
113
+ # @param client [LDClient] The LDClient instance
114
+ # @param environment_metadata [EnvironmentMetadata] Metadata about the environment in which the SDK is running
115
+ # @return [void]
116
+ #
117
+ def register(client, environment_metadata)
118
+ # Default implementation does nothing
119
+ end
120
+
121
+ #
122
+ # Get a list of hooks that this plugin provides.
123
+ #
124
+ # This method is called before register() to collect all hooks from plugins. The hooks returned will be
125
+ # added to the SDK's hook configuration.
126
+ #
127
+ # @param environment_metadata [EnvironmentMetadata] Metadata about the environment in which the SDK is running
128
+ # @return [Array<Interfaces::Hooks::Hook>] A list of hooks to be registered with the SDK
129
+ #
130
+ def get_hooks(environment_metadata)
131
+ []
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end