ibm_appconfiguration_ruby_sdk 0.1.0.pre.rc.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +76 -0
- data/CONTRIBUTING.md +9 -0
- data/LICENSE +201 -0
- data/README.md +474 -0
- data/Rakefile +8 -0
- data/examples/README.md +60 -0
- data/examples/app.rb +104 -0
- data/lib/ibm_appconfiguration_ruby_sdk/app_configuration.rb +291 -0
- data/lib/ibm_appconfiguration_ruby_sdk/configurations/configuration_handler.rb +828 -0
- data/lib/ibm_appconfiguration_ruby_sdk/configurations/internal/constants.rb +89 -0
- data/lib/ibm_appconfiguration_ruby_sdk/configurations/internal/file_manager.rb +72 -0
- data/lib/ibm_appconfiguration_ruby_sdk/configurations/internal/logger.rb +98 -0
- data/lib/ibm_appconfiguration_ruby_sdk/configurations/internal/retry_manager/background_retry_manager.rb +284 -0
- data/lib/ibm_appconfiguration_ruby_sdk/configurations/internal/retry_manager/config_fetcher.rb +254 -0
- data/lib/ibm_appconfiguration_ruby_sdk/configurations/internal/utils.rb +240 -0
- data/lib/ibm_appconfiguration_ruby_sdk/configurations/internal/websocket_client/connection_manager.rb +501 -0
- data/lib/ibm_appconfiguration_ruby_sdk/configurations/internal/websocket_client/connectivity.rb +30 -0
- data/lib/ibm_appconfiguration_ruby_sdk/configurations/internal/websocket_client/driver_socket.rb +28 -0
- data/lib/ibm_appconfiguration_ruby_sdk/configurations/internal/websocket_client/retry_policy.rb +42 -0
- data/lib/ibm_appconfiguration_ruby_sdk/configurations/internal/websocket_client/state.rb +24 -0
- data/lib/ibm_appconfiguration_ruby_sdk/configurations/internal/websocket_client/watchdog.rb +50 -0
- data/lib/ibm_appconfiguration_ruby_sdk/configurations/internal/websocket_client/websocket_client.rb +43 -0
- data/lib/ibm_appconfiguration_ruby_sdk/configurations/models/feature.rb +121 -0
- data/lib/ibm_appconfiguration_ruby_sdk/configurations/models/property.rb +107 -0
- data/lib/ibm_appconfiguration_ruby_sdk/configurations/models/rule.rb +87 -0
- data/lib/ibm_appconfiguration_ruby_sdk/configurations/models/secret_property.rb +81 -0
- data/lib/ibm_appconfiguration_ruby_sdk/configurations/models/segment.rb +39 -0
- data/lib/ibm_appconfiguration_ruby_sdk/configurations/models/segment_rules.rb +57 -0
- data/lib/ibm_appconfiguration_ruby_sdk/core/api_manager.rb +269 -0
- data/lib/ibm_appconfiguration_ruby_sdk/core/metering.rb +400 -0
- data/lib/ibm_appconfiguration_ruby_sdk/core/url_builder.rb +252 -0
- data/lib/ibm_appconfiguration_ruby_sdk/version.rb +20 -0
- data/lib/ibm_appconfiguration_ruby_sdk.rb +20 -0
- data/sig/ibm_appconfiguration_ruby_sdk.rbs +4 -0
- metadata +209 -0
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
# Copyright 2026 IBM Corp. All Rights Reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
# frozen_string_literal: true
|
|
16
|
+
|
|
17
|
+
require "singleton"
|
|
18
|
+
require_relative "configurations/configuration_handler"
|
|
19
|
+
require_relative "configurations/internal/logger"
|
|
20
|
+
require_relative "configurations/internal/constants"
|
|
21
|
+
require_relative "core/url_builder"
|
|
22
|
+
|
|
23
|
+
module IbmAppconfigurationRubySdk
|
|
24
|
+
# AppConfiguration client class implementing singleton pattern
|
|
25
|
+
class AppConfiguration
|
|
26
|
+
include Singleton
|
|
27
|
+
|
|
28
|
+
# Region constants
|
|
29
|
+
REGION_US_SOUTH = "us-south"
|
|
30
|
+
REGION_EU_GB = "eu-gb"
|
|
31
|
+
REGION_AU_SYD = "au-syd"
|
|
32
|
+
REGION_US_EAST = "us-east"
|
|
33
|
+
REGION_EU_DE = "eu-de"
|
|
34
|
+
REGION_CA_TOR = "ca-tor"
|
|
35
|
+
REGION_JP_TOK = "jp-tok"
|
|
36
|
+
REGION_JP_OSA = "jp-osa"
|
|
37
|
+
|
|
38
|
+
class << self
|
|
39
|
+
##
|
|
40
|
+
# Get the current instance without creating a new one
|
|
41
|
+
# @return [AppConfiguration] The current instance
|
|
42
|
+
# @raise [RuntimeError] If instance doesn't exist
|
|
43
|
+
def current_instance
|
|
44
|
+
raise Constants::SINGLETON_EXCEPTION unless @singleton__instance__
|
|
45
|
+
|
|
46
|
+
instance
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
##
|
|
50
|
+
# Override the default App Configuration URL
|
|
51
|
+
# This method should be invoked before the SDK initialization
|
|
52
|
+
# NOTE: To be used for development purposes only
|
|
53
|
+
# @param url [String] The base service URL
|
|
54
|
+
def override_service_url(url)
|
|
55
|
+
return unless url
|
|
56
|
+
|
|
57
|
+
url_builder = UrlBuilder.instance
|
|
58
|
+
url_builder.set_base_service_url(url)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def initialize
|
|
63
|
+
@is_initialized = false
|
|
64
|
+
@is_initialized_config = false
|
|
65
|
+
@use_private_endpoint = false
|
|
66
|
+
@configuration_handler = nil
|
|
67
|
+
@logger = Logger.instance
|
|
68
|
+
@url_builder = UrlBuilder.instance
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
##
|
|
72
|
+
# Initialize the SDK to connect with your App Configuration service instance
|
|
73
|
+
# @param region [String] Region name where the App Configuration service instance is created
|
|
74
|
+
# @param guid [String] GUID of the App Configuration service
|
|
75
|
+
# @param apikey [String] API key of the App Configuration service
|
|
76
|
+
# @raise [RuntimeError] If any required parameter is missing or invalid
|
|
77
|
+
def init(region, guid, apikey)
|
|
78
|
+
# init is a SDK initialization method. It is expected to be called only once.
|
|
79
|
+
# This condition ensures the init inputs are taken only once even if called multiple times.
|
|
80
|
+
return if @is_initialized
|
|
81
|
+
|
|
82
|
+
unless region && guid && apikey
|
|
83
|
+
if !region
|
|
84
|
+
report_error(Constants::REGION_ERROR)
|
|
85
|
+
elsif !guid
|
|
86
|
+
report_error(Constants::GUID_ERROR)
|
|
87
|
+
else
|
|
88
|
+
report_error(Constants::APIKEY_ERROR)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
@configuration_handler = ConfigurationHandler.instance
|
|
93
|
+
@configuration_handler.init(region, guid, apikey, @use_private_endpoint)
|
|
94
|
+
@is_initialized = true
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
##
|
|
98
|
+
# Set the context of the SDK
|
|
99
|
+
# @param collection_id [String] ID of the collection created in App Configuration service instance
|
|
100
|
+
# @param environment_id [String] ID of the environment created in App Configuration service instance
|
|
101
|
+
# @param options [Hash] Optional configuration parameters
|
|
102
|
+
# @option options [String] :persistent_cache_directory Directory path for persistent cache
|
|
103
|
+
# @option options [String] :bootstrap_file Absolute path of configuration file
|
|
104
|
+
# @option options [Boolean] :live_config_update_enabled Enable live configuration updates (default: true)
|
|
105
|
+
# @raise [RuntimeError] If init was not called or parameters are invalid
|
|
106
|
+
def set_context(collection_id, environment_id, options = {})
|
|
107
|
+
# setContext is also a SDK initialization method. It is expected to be called only once.
|
|
108
|
+
# This condition ensures the setContext inputs are taken only once even if called multiple times.
|
|
109
|
+
return if @is_initialized_config
|
|
110
|
+
|
|
111
|
+
report_error(Constants::COLLECTION_ID_ERROR) unless @is_initialized
|
|
112
|
+
|
|
113
|
+
report_error(Constants::COLLECTION_ID_VALUE_ERROR) unless collection_id
|
|
114
|
+
|
|
115
|
+
report_error(Constants::ENVIRONMENT_ID_VALUE_ERROR) unless environment_id
|
|
116
|
+
|
|
117
|
+
default_options = {
|
|
118
|
+
persistent_cache_directory: nil,
|
|
119
|
+
bootstrap_file: nil,
|
|
120
|
+
live_config_update_enabled: true
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if options
|
|
124
|
+
report_error(Constants::INVALID_OPTIONS_PARAMETER.to_s) unless options.is_a?(Hash)
|
|
125
|
+
|
|
126
|
+
if options.key?(:persistent_cache_directory)
|
|
127
|
+
given_dir_path = options[:persistent_cache_directory]
|
|
128
|
+
if given_dir_path.is_a?(String) && !given_dir_path.empty?
|
|
129
|
+
default_options[:persistent_cache_directory] = given_dir_path
|
|
130
|
+
else
|
|
131
|
+
report_error("#{Constants::PERSISTENT_CACHE_OPTION_ERROR} #{given_dir_path}")
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
if options.key?(:bootstrap_file)
|
|
136
|
+
given_file_path = options[:bootstrap_file]
|
|
137
|
+
if given_file_path.is_a?(String) && !given_file_path.empty? && File.extname(given_file_path) == ".json"
|
|
138
|
+
default_options[:bootstrap_file] = given_file_path
|
|
139
|
+
else
|
|
140
|
+
report_error("#{Constants::BOOTSTRAP_FILEPATH_OPTION_ERROR} #{given_file_path}")
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
if options.key?(:live_config_update_enabled)
|
|
145
|
+
given_flag_value = options[:live_config_update_enabled]
|
|
146
|
+
if [true, false].include?(given_flag_value)
|
|
147
|
+
default_options[:live_config_update_enabled] = given_flag_value
|
|
148
|
+
else
|
|
149
|
+
report_error("#{Constants::LIVE_CONFIG_UPDATE_OPTION_ERROR} #{given_flag_value}")
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
report_error(Constants::CONFIGURATION_FILE_NOT_FOUND_ERROR) if default_options[:live_config_update_enabled] == false && default_options[:bootstrap_file].nil?
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
@is_initialized_config = true
|
|
157
|
+
@configuration_handler = ConfigurationHandler.instance
|
|
158
|
+
@configuration_handler.set_context(collection_id, environment_id, default_options)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
##
|
|
162
|
+
# Set the SDK to connect to App Configuration service using a private endpoint
|
|
163
|
+
# This function must be called before calling the init function
|
|
164
|
+
# @param use_private_endpoint_param [Boolean] Set to true to use private endpoint (default: false)
|
|
165
|
+
def use_private_endpoint(use_private_endpoint_param)
|
|
166
|
+
if [true, false].include?(use_private_endpoint_param)
|
|
167
|
+
@use_private_endpoint = use_private_endpoint_param
|
|
168
|
+
return
|
|
169
|
+
end
|
|
170
|
+
@logger.error(Constants::INPUT_PARAMETER_NOT_BOOLEAN)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
##
|
|
174
|
+
# Returns the Feature object with the details of the feature specified by the feature_id
|
|
175
|
+
# @param feature_id [String] The Feature ID
|
|
176
|
+
# @return [Feature, nil] Feature object or nil if invalid
|
|
177
|
+
def get_feature(feature_id)
|
|
178
|
+
return @configuration_handler.get_feature(feature_id) if @is_initialized && @is_initialized_config
|
|
179
|
+
|
|
180
|
+
@logger.error(Constants::COLLECTION_INIT_ERROR)
|
|
181
|
+
nil
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
##
|
|
185
|
+
# Returns all features associated with the collection_id
|
|
186
|
+
# @return [Hash, nil] Hash of all features or nil
|
|
187
|
+
def get_features
|
|
188
|
+
return @configuration_handler.get_features if @is_initialized && @is_initialized_config
|
|
189
|
+
|
|
190
|
+
@logger.error(Constants::COLLECTION_INIT_ERROR)
|
|
191
|
+
nil
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
##
|
|
195
|
+
# Returns the Property object with the details of the property specified by the property_id
|
|
196
|
+
# @param property_id [String] The Property ID
|
|
197
|
+
# @return [Property, nil] Property object or nil if invalid
|
|
198
|
+
def get_property(property_id)
|
|
199
|
+
return @configuration_handler.get_property(property_id) if @is_initialized && @is_initialized_config
|
|
200
|
+
|
|
201
|
+
@logger.error(Constants::COLLECTION_INIT_ERROR)
|
|
202
|
+
nil
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
##
|
|
206
|
+
# Returns all properties associated with the collection_id
|
|
207
|
+
# @return [Hash, nil] Hash of all properties or nil
|
|
208
|
+
def get_properties
|
|
209
|
+
return @configuration_handler.get_properties if @is_initialized && @is_initialized_config
|
|
210
|
+
|
|
211
|
+
@logger.error(Constants::COLLECTION_INIT_ERROR)
|
|
212
|
+
nil
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
##
|
|
216
|
+
# Returns the SecretProperty object corresponding to the given property_id
|
|
217
|
+
# @param property_id [String] ID of the secret property from App Configuration
|
|
218
|
+
# @param secrets_manager_service [Object] Secret Manager client object
|
|
219
|
+
# @return [SecretProperty, nil] SecretProperty object or nil
|
|
220
|
+
def get_secret(property_id, secrets_manager_service)
|
|
221
|
+
if @is_initialized && @is_initialized_config
|
|
222
|
+
return @configuration_handler.get_secret(property_id, secrets_manager_service) if secrets_manager_service
|
|
223
|
+
|
|
224
|
+
@logger.error(Constants::INVALID_SECRET_MANAGER_CLIENT_MESSAGE)
|
|
225
|
+
return nil
|
|
226
|
+
end
|
|
227
|
+
@logger.error(Constants::COLLECTION_INIT_ERROR)
|
|
228
|
+
nil
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
##
|
|
232
|
+
# Record custom metric events for an entity_id while running an experiment
|
|
233
|
+
# @param event_key [String] SDK event key
|
|
234
|
+
# @param entity_id [String] The entity ID
|
|
235
|
+
def track(event_key, entity_id)
|
|
236
|
+
return @configuration_handler.track(event_key, entity_id) if @is_initialized && @is_initialized_config
|
|
237
|
+
|
|
238
|
+
@logger.error(Constants::COLLECTION_INIT_ERROR)
|
|
239
|
+
nil
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
##
|
|
243
|
+
# Enable or disable the logger
|
|
244
|
+
# By default, logger is disabled
|
|
245
|
+
# @param value [Boolean] Enable (true) or disable (false) debug logging
|
|
246
|
+
def set_debug(value = false)
|
|
247
|
+
Logger.set_debug(value)
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
##
|
|
251
|
+
# Check if the SDK is connected to the service
|
|
252
|
+
# @return [Boolean] Connection status
|
|
253
|
+
def connected?
|
|
254
|
+
return @configuration_handler.connected? if @is_initialized && @is_initialized_config
|
|
255
|
+
|
|
256
|
+
false
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
##
|
|
260
|
+
# Register a configuration update listener
|
|
261
|
+
# The listener will be invoked whenever configurations are updated (initial load or live updates).
|
|
262
|
+
# Only one listener can be registered at a time - calling this method multiple times will replace the previous listener.
|
|
263
|
+
# @param block [Proc] Callback block to be invoked on configuration updates
|
|
264
|
+
# @example
|
|
265
|
+
# app_config = IbmAppconfigurationRubySdk::AppConfiguration.instance
|
|
266
|
+
# app_config.register_configuration_update_listener do
|
|
267
|
+
# puts "Configurations updated!"
|
|
268
|
+
# feature = app_config.get_feature('my-feature')
|
|
269
|
+
# # React to configuration changes
|
|
270
|
+
# end
|
|
271
|
+
# @return [void]
|
|
272
|
+
def register_configuration_update_listener(&block)
|
|
273
|
+
if @is_initialized && @is_initialized_config
|
|
274
|
+
@configuration_handler.register_configuration_update_listener(&block)
|
|
275
|
+
return
|
|
276
|
+
end
|
|
277
|
+
@logger.error(Constants::COLLECTION_INIT_ERROR)
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
private
|
|
281
|
+
|
|
282
|
+
##
|
|
283
|
+
# Report error and raise exception
|
|
284
|
+
# @param error [String] Error message
|
|
285
|
+
# @raise [RuntimeError] Always raises with the error message
|
|
286
|
+
def report_error(error)
|
|
287
|
+
@logger.error(error)
|
|
288
|
+
raise error
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
end
|