Class: ConfigurationHandler
- Inherits:
-
Object
- Object
- ConfigurationHandler
- Includes:
- Singleton
- Defined in:
- lib/ibm_appconfiguration_ruby_sdk/configurations/configuration_handler.rb
Overview
Internal client to handle the configuration
Instance Method Summary collapse
-
#cleanup ⇒ Object
Cleanup resources.
-
#connected? ⇒ Boolean
Check if connected.
-
#evaluate_rules(rules_map, entity_attributes, feature, property, entity_id = nil) ⇒ Hash
Evaluate rules.
-
#evaluate_segment(segment_key, entity_attributes) ⇒ Boolean
Evaluate segment.
-
#feature_evaluation(feature, entity_id, entity_attributes) ⇒ Hash
Feature evaluation.
- #format_config(configurations, _environment_id, _collection_id) ⇒ Object
-
#get_feature(feature_id) ⇒ Feature?
Get feature by ID.
-
#get_features ⇒ Hash
Get features.
-
#get_properties ⇒ Hash
Get properties.
-
#get_property(property_id) ⇒ Property?
Get property by ID.
-
#get_rollout_percentage(feature, segment_rule, _entity_id) ⇒ Integer
Get rollout percentage for progressive rollout.
-
#get_secret(property_id, secrets_manager_service) ⇒ SecretProperty?
Get secret property.
-
#get_secrets_map ⇒ Hash
Get the secrets map.
-
#get_segment(segment_id) ⇒ Segment?
Get segment by ID.
-
#init(region, guid, apikey, use_private_endpoint) ⇒ Object
Initialize the configuration handler.
-
#initialize ⇒ ConfigurationHandler
constructor
A new instance of ConfigurationHandler.
-
#load_configurations_to_cache(data) ⇒ Object
Load configurations to cache.
-
#notify_configuration_update_listener ⇒ Object
Notify the registered configuration update listener This method is called internally when configurations are updated.
-
#parse_rules(segment_rules) ⇒ Hash
Parse rules.
-
#property_evaluation(property, entity_id, entity_attributes) ⇒ Hash
Property evaluation.
-
#record_evaluation(feature_id, property_id, entity_id, segment_id) ⇒ Object
Record evaluation.
-
#register_configuration_update_listener(&block) ⇒ Object
Register configuration update listener Registers a callback block that will be invoked when configurations are updated.
-
#report_error(error) ⇒ Object
Report error.
-
#set_bootstrap_file(bootstrap_file) ⇒ Object
Set bootstrap file.
-
#set_context(collection_id, environment_id, options = {}) ⇒ Object
Set context for configuration.
-
#set_live_update(live_update) ⇒ Object
Set live update status.
-
#set_persistent_cache_directory(directory) ⇒ Object
Set persistent cache directory.
- #track(event_key, entity_id) ⇒ Object
-
#write_to_persistent_storage(file_data) ⇒ Object
Write to persistent storage.
Constructor Details
#initialize ⇒ ConfigurationHandler
Returns a new instance of ConfigurationHandler.
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/ibm_appconfiguration_ruby_sdk/configurations/configuration_handler.rb', line 38 def initialize @collection_id = nil @environment_id = nil @guid = nil @is_connected = true @live_update = true @bootstrap_file = nil @persistent_cache_directory = nil @feature_map = {} @property_map = {} @segment_map = {} @secret_map = {} @rollout_config_map = {} @all_feature_flags = [] @logger = Logger.instance @file_manager = FileManager.instance @websocket_client = nil # Configuration update listener (single listener, matches Java SDK) @configuration_update_listener = nil end |
Instance Method Details
#cleanup ⇒ Object
Cleanup resources
91 92 93 94 |
# File 'lib/ibm_appconfiguration_ruby_sdk/configurations/configuration_handler.rb', line 91 def cleanup Metering.instance.cleanup @websocket_client&.disconnect end |
#connected? ⇒ Boolean
Check if connected
761 762 763 |
# File 'lib/ibm_appconfiguration_ruby_sdk/configurations/configuration_handler.rb', line 761 def connected? @is_connected end |
#evaluate_rules(rules_map, entity_attributes, feature, property, entity_id = nil) ⇒ Hash
Evaluate rules
521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 |
# File 'lib/ibm_appconfiguration_ruby_sdk/configurations/configuration_handler.rb', line 521 def evaluate_rules(rules_map, entity_attributes, feature, property, entity_id = nil) result_dict = { evaluated_segment_id: Constants::DEFAULT_SEGMENT_ID, value: nil, is_enabled: false, details: {} } begin # For each rule in the targeting (1..rules_map.keys.length).each do |index| segment_rule = rules_map[index] next unless segment_rule.get_rules.length.positive? segment_rule.get_rules.each do |rule| segments = rule[:segments] next unless segments&.length&.positive? # For each segment in a rule segments.each do |segment_key| # Check whether the entityAttributes satisfies all the rules of that segment next unless evaluate_segment(segment_key, entity_attributes) segment_name = @segment_map[segment_key].name result_dict[:evaluated_segment_id] = segment_key result_dict[:details][:segment_name] = segment_name if feature # evaluateRules was called for feature flag segment_level_rollout_percentage = get_rollout_percentage(feature, segment_rule, entity_id) # Check whether the entityId is eligible for segment rollout if segment_level_rollout_percentage == 100 || (entity_id && get_normalized_value("#{entity_id}:#{feature.feature_id}") < segment_level_rollout_percentage) # Since the entityId is eligible for segment rollout, return inherited or overridden value result_dict[:value] = if segment_rule.get_value == Constants::DEFAULT_FEATURE_VALUE feature.enabled_value # Return the inherited value else segment_rule.get_value # Return the overridden value end result_dict[:details][:value_type] = "SEGMENT_VALUE" result_dict[:is_enabled] = true result_dict[:details][:rollout_percentage_applied] = true else result_dict[:value] = feature.disabled_value result_dict[:is_enabled] = false result_dict[:details][:value_type] = "DISABLED_VALUE" result_dict[:details][:rollout_percentage_applied] = false end else # evaluateRules was called for property result_dict[:value] = if segment_rule.get_value == Constants::DEFAULT_PROPERTY_VALUE property.value else segment_rule.get_value end result_dict[:details][:value_type] = "SEGMENT_VALUE" end return result_dict end end end rescue StandardError => e @logger.error("RuleEvaluation #{e}") result_dict[:value] = nil result_dict[:is_enabled] = false result_dict[:details][:value_type] = "ERROR" result_dict[:details][:error_type] = e. return result_dict end # Since entityAttributes did not satisfy any of the targeting rules if feature # evaluateRules was called for feature flag # Check whether the entityId is eligible for default rollout rollout_percentage = get_rollout_percentage(feature, nil, entity_id) if rollout_percentage == 100 || (entity_id && get_normalized_value("#{entity_id}:#{feature.feature_id}") < rollout_percentage) result_dict[:value] = feature.enabled_value result_dict[:is_enabled] = true result_dict[:details][:value_type] = "ENABLED_VALUE" result_dict[:details][:rollout_percentage_applied] = true else result_dict[:value] = feature.disabled_value result_dict[:is_enabled] = false result_dict[:details][:value_type] = "DISABLED_VALUE" result_dict[:details][:rollout_percentage_applied] = false end else # evaluateRules was called for property result_dict[:value] = property.value result_dict[:details][:value_type] = "DEFAULT_VALUE" end result_dict end |
#evaluate_segment(segment_key, entity_attributes) ⇒ Boolean
Evaluate segment
439 440 441 442 443 444 445 |
# File 'lib/ibm_appconfiguration_ruby_sdk/configurations/configuration_handler.rb', line 439 def evaluate_segment(segment_key, entity_attributes) if @segment_map.key?(segment_key) segment_obj = @segment_map[segment_key] return segment_obj.evaluate_rule(entity_attributes) end nil end |
#feature_evaluation(feature, entity_id, entity_attributes) ⇒ Hash
Feature evaluation
645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 |
# File 'lib/ibm_appconfiguration_ruby_sdk/configurations/configuration_handler.rb', line 645 def feature_evaluation(feature, entity_id, entity_attributes) result_dict = { evaluated_segment_id: Constants::DEFAULT_SEGMENT_ID, value: nil, is_enabled: false, details: {} } begin # Step 1: Check if feature flag is enabled unless feature.enabled result_dict[:details][:value_type] = "DISABLED_VALUE" return { value: feature.disabled_value, is_enabled: false, details: result_dict[:details] } end # Step 2: Check if feature has segment rules (targeting) and valid entity attributes if feature.segment_rules&.length&.positive? && entity_attributes.is_a?(Hash) && entity_attributes.keys.length.positive? # Evaluate targeting rules rules_map = parse_rules(feature.segment_rules) result_dict = evaluate_rules(rules_map, entity_attributes, feature, nil, entity_id) return { value: result_dict[:value], is_enabled: result_dict[:is_enabled], details: result_dict[:details] } end # Step 3: No targeting rules - apply default rollout percentage # Check if entity_id qualifies for rollout rollout_percentage = get_rollout_percentage(feature, nil, entity_id) normalized_value = get_normalized_value("#{entity_id}:#{feature.feature_id}") if rollout_percentage == 100 || normalized_value < rollout_percentage result_dict[:details][:value_type] = "ENABLED_VALUE" result_dict[:details][:rollout_percentage_applied] = true return { value: feature.enabled_value, is_enabled: true, details: result_dict[:details] } end # Step 4: Entity doesn't qualify for rollout result_dict[:details][:value_type] = "DISABLED_VALUE" result_dict[:details][:rollout_percentage_applied] = false { value: feature.disabled_value, is_enabled: false, details: result_dict[:details] } ensure # Always record evaluation for metering record_evaluation(feature.feature_id, nil, entity_id, result_dict[:evaluated_segment_id]) end end |
#format_config(configurations, _environment_id, _collection_id) ⇒ Object
170 171 172 173 |
# File 'lib/ibm_appconfiguration_ruby_sdk/configurations/configuration_handler.rb', line 170 def format_config(configurations, _environment_id, _collection_id) # TODO: Implement actual formatting logic configurations end |
#get_feature(feature_id) ⇒ Feature?
Get feature by ID
387 388 389 390 391 392 |
# File 'lib/ibm_appconfiguration_ruby_sdk/configurations/configuration_handler.rb', line 387 def get_feature(feature_id) return @feature_map[feature_id] if @feature_map.key?(feature_id) @logger.error("Invalid feature id - #{feature_id}") nil end |
#get_features ⇒ Hash
Get features
747 748 749 |
# File 'lib/ibm_appconfiguration_ruby_sdk/configurations/configuration_handler.rb', line 747 def get_features @feature_map end |
#get_properties ⇒ Hash
Get properties
754 755 756 |
# File 'lib/ibm_appconfiguration_ruby_sdk/configurations/configuration_handler.rb', line 754 def get_properties @property_map end |
#get_property(property_id) ⇒ Property?
Get property by ID
398 399 400 401 402 403 |
# File 'lib/ibm_appconfiguration_ruby_sdk/configurations/configuration_handler.rb', line 398 def get_property(property_id) return @property_map[property_id] if @property_map.key?(property_id) @logger.error("Invalid property id - #{property_id}") nil end |
#get_rollout_percentage(feature, segment_rule, _entity_id) ⇒ Integer
Get rollout percentage for progressive rollout
465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 |
# File 'lib/ibm_appconfiguration_ruby_sdk/configurations/configuration_handler.rb', line 465 def get_rollout_percentage(feature, segment_rule, _entity_id) if segment_rule # Segment-level rollout if segment_rule.rollout_configuration || (segment_rule.rollout_type && segment_rule.rollout_type == Constants::PROGRESSIVE) rollout_hash = if segment_rule.rollout_percentage == Constants::DEFAULT_ROLLOUT_PERCENTAGE @rollout_config_map[feature.feature_id] else @rollout_config_map["#{feature.feature_id}#{Constants::DELIMITER}#{segment_rule.rule_id}"] end return 0 unless rollout_hash segment_rule.rollout_configuration[:start_at] current_time_ms = (Time.now.to_f * 1000).to_i # Find the entry with timestamp <= current time (sorted hash) percentage = 0 rollout_hash.each do |, pct| break if > current_time_ms percentage = pct end percentage else # Manual rollout segment_rule.rollout_percentage == Constants::DEFAULT_ROLLOUT_PERCENTAGE ? feature.rollout_percentage : segment_rule.rollout_percentage end else # Feature-level rollout return feature.rollout_percentage || 100 unless feature.rollout_configuration rollout_hash = @rollout_config_map[feature.feature_id] return 0 unless rollout_hash feature.rollout_configuration[:start_at] current_time_ms = (Time.now.to_f * 1000).to_i # Find the entry with timestamp <= current time (sorted hash) percentage = 0 rollout_hash.each do |, pct| break if > current_time_ms percentage = pct end percentage end end |
#get_secret(property_id, secrets_manager_service) ⇒ SecretProperty?
Get secret property
410 411 412 413 414 415 416 417 418 419 420 421 |
# File 'lib/ibm_appconfiguration_ruby_sdk/configurations/configuration_handler.rb', line 410 def get_secret(property_id, secrets_manager_service) property_obj = get_property(property_id) if property_obj if property_obj.get_property_data_type == Constants::SECRETREF @secret_map[property_id] = secrets_manager_service return SecretProperty.new(property_id) end @logger.error("Invalid operation: getSecret() cannot be called on a #{property_obj.get_property_data_type} property.") return nil end nil end |
#get_secrets_map ⇒ Hash
Get the secrets map
825 826 827 |
# File 'lib/ibm_appconfiguration_ruby_sdk/configurations/configuration_handler.rb', line 825 def get_secrets_map @secret_map end |
#get_segment(segment_id) ⇒ Segment?
Get segment by ID
427 428 429 430 431 432 |
# File 'lib/ibm_appconfiguration_ruby_sdk/configurations/configuration_handler.rb', line 427 def get_segment(segment_id) return @segment_map[segment_id] if @segment_map.key?(segment_id) @logger.error("Invalid segment id - #{segment_id}") nil end |
#init(region, guid, apikey, use_private_endpoint) ⇒ Object
Initialize the configuration handler
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/ibm_appconfiguration_ruby_sdk/configurations/configuration_handler.rb', line 68 def init(region, guid, apikey, use_private_endpoint) @guid = guid @region = region @apikey = apikey @use_private_endpoint = use_private_endpoint # Initialize UrlBuilder url_builder = UrlBuilder.instance url_builder.region = region url_builder.guid = guid url_builder.apikey = apikey url_builder.use_private_endpoint = use_private_endpoint # Initialize ApiManager ApiManager.set_authenticator # Initialize Metering metering_url = "#{url_builder.base_service_url}/apprapp/events/v1/instances/#{guid}/usage" Metering.instance.set_metering_url(metering_url, apikey) end |
#load_configurations_to_cache(data) ⇒ Object
Load configurations to cache
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/ibm_appconfiguration_ruby_sdk/configurations/configuration_handler.rb', line 99 def load_configurations_to_cache(data) return unless data if data[:features] features = data[:features] @all_feature_flags = features @feature_map = {} @rollout_config_map = {} features.each do |feature| feature_obj = Feature.new(feature) @feature_map[feature[:feature_id]] = feature_obj # Parse feature-level progressive rollout if feature_obj.rollout_configuration @rollout_config_map[feature[:feature_id]] = parse_rollout_configuration_phases(feature_obj.rollout_configuration) end # Parse segment-level progressive rollout next unless feature[:segment_rules].is_a?(Array) feature[:segment_rules].each do |segment_rule| segment_rule_obj = SegmentRules.new(segment_rule) if segment_rule_obj.rollout_configuration key = "#{feature[:feature_id]}#{Constants::DELIMITER}#{segment_rule[:rule_id]}" @rollout_config_map[key] = parse_rollout_configuration_phases(segment_rule_obj.rollout_configuration) end end end end if data[:properties] properties = data[:properties] @property_map = {} properties.each do |property| @property_map[property[:property_id]] = Property.new(property) end end if data[:segments] segments = data[:segments] @segment_map = {} segments.each do |segment| @segment_map[segment[:segment_id]] = Segment.new(segment) end end # Notify listener after configurations are loaded notify_configuration_update_listener end |
#notify_configuration_update_listener ⇒ Object
Notify the registered configuration update listener This method is called internally when configurations are updated. The listener is invoked safely - exceptions are caught to prevent breaking the update flow.
810 811 812 813 814 815 816 817 818 819 820 |
# File 'lib/ibm_appconfiguration_ruby_sdk/configurations/configuration_handler.rb', line 810 def notify_configuration_update_listener return unless @configuration_update_listener begin @logger.log("Notifying configuration update listener") @configuration_update_listener.call rescue StandardError => e @logger.error("Error in configuration update listener: #{e.}") @logger.error(e.backtrace.first(3).join("\n")) end end |
#parse_rules(segment_rules) ⇒ Hash
Parse rules
451 452 453 454 455 456 457 |
# File 'lib/ibm_appconfiguration_ruby_sdk/configurations/configuration_handler.rb', line 451 def parse_rules(segment_rules) rules_map = {} segment_rules.each do |rules| rules_map[rules[:order]] = SegmentRules.new(rules) end rules_map end |
#property_evaluation(property, entity_id, entity_attributes) ⇒ Hash
Property evaluation
713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 |
# File 'lib/ibm_appconfiguration_ruby_sdk/configurations/configuration_handler.rb', line 713 def property_evaluation(property, entity_id, entity_attributes) result_dict = { evaluated_segment_id: Constants::DEFAULT_SEGMENT_ID, value: nil, details: {} } begin # Check whether the property is configured with any targeting definition # and then check whether the user has passed valid entityAttributes JSON before we evaluate if property.segment_rules&.length&.positive? && entity_attributes.is_a?(Hash) && entity_attributes.keys.length.positive? rules_map = parse_rules(property.segment_rules) result_dict = evaluate_rules(rules_map, entity_attributes, nil, property, entity_id) return { value: result_dict[:value], details: result_dict[:details] } end result_dict[:details][:value_type] = "DEFAULT_VALUE" { value: property.value, details: result_dict[:details] } ensure # Record evaluation for metering record_evaluation(nil, property.property_id, entity_id, result_dict[:evaluated_segment_id]) end end |
#record_evaluation(feature_id, property_id, entity_id, segment_id) ⇒ Object
Record evaluation
627 628 629 630 631 632 633 634 635 636 637 |
# File 'lib/ibm_appconfiguration_ruby_sdk/configurations/configuration_handler.rb', line 627 def record_evaluation(feature_id, property_id, entity_id, segment_id) Metering.instance.add_metering( @guid, @environment_id, @collection_id, entity_id || Constants::DEFAULT_ENTITY_ID, segment_id || Constants::DEFAULT_SEGMENT_ID, feature_id, property_id ) end |
#register_configuration_update_listener(&block) ⇒ Object
Register configuration update listener Registers a callback block that will be invoked when configurations are updated. Only one listener can be registered at a time (matches Java SDK behavior). Calling this method multiple times will replace the previous listener.
796 797 798 799 800 801 802 803 |
# File 'lib/ibm_appconfiguration_ruby_sdk/configurations/configuration_handler.rb', line 796 def register_configuration_update_listener(&block) if block_given? @configuration_update_listener = block @logger.log("Configuration update listener registered") else @logger.warning("No block provided to register_configuration_update_listener") end end |
#report_error(error) ⇒ Object
Report error
165 166 167 168 |
# File 'lib/ibm_appconfiguration_ruby_sdk/configurations/configuration_handler.rb', line 165 def report_error(error) error_msg = error.is_a?(Exception) ? error. : error.to_s @logger.error(error_msg) end |
#set_bootstrap_file(bootstrap_file) ⇒ Object
Set bootstrap file
775 776 777 |
# File 'lib/ibm_appconfiguration_ruby_sdk/configurations/configuration_handler.rb', line 775 def set_bootstrap_file(bootstrap_file) @bootstrap_file = bootstrap_file end |
#set_context(collection_id, environment_id, options = {}) ⇒ Object
Set context for configuration
180 181 182 183 184 185 186 187 188 189 190 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 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 |
# File 'lib/ibm_appconfiguration_ruby_sdk/configurations/configuration_handler.rb', line 180 def set_context(collection_id, environment_id, = {}) @collection_id = collection_id @environment_id = environment_id @persistent_cache_directory = [:persistent_cache_directory] @bootstrap_file = [:bootstrap_file] @live_update = [:live_config_update_enabled] # TODO: Initialize evaluation events and metric events # evaluationEvents.init(@guid, @environment_id) # metricEvents.init(@guid, @environment_id) persistent_cache_read = false error_reading_bootstrap_config = false # Handle persistent cache directory if @persistent_cache_directory @logger.info("persistent cache directory path is: #{@persistent_cache_directory}") file_path = File.join(@persistent_cache_directory, "appconfiguration.json") persistent_cache = @file_manager.read_persistent_cache_configurations(file_path) unless persistent_cache.empty? configurations = extract_configurations(JSON.parse(persistent_cache), @environment_id, @collection_id) load_configurations_to_cache(configurations) persistent_cache_read = true end # Check write permissions begin # Test if directory is writable File.write(File.join(@persistent_cache_directory, ".write_test"), "") File.delete(File.join(@persistent_cache_directory, ".write_test")) rescue StandardError => e report_error("ERROR: No write permission for persistent cache directory. #{e}") end end # Handle bootstrap file if @bootstrap_file if @persistent_cache_directory # If persistent cache directory exists if persistent_cache_read # Persistent cache was read, emit event if not live update # TODO: emit(APPCONFIGURATION_CLIENT_EMITTER) unless @live_update else # Only read bootstrap if persistent cache wasn't read begin @logger.info("reading configurations from bootstrap file: #{@bootstrap_file}") bootstrap_config = @file_manager.read_bootstrap_configurations_from_file(@bootstrap_file) configurations = extract_configurations(JSON.parse(bootstrap_config), @environment_id, @collection_id) load_configurations_to_cache(configurations) write_to_persistent_storage(format_config(configurations, @environment_id, @collection_id)) # TODO: emit event if not live update # emit(APPCONFIGURATION_CLIENT_EMITTER) unless @live_update rescue StandardError => e report_error(e) end end else # No persistent cache directory, just read bootstrap @logger.info("reading configurations from bootstrap file: #{@bootstrap_file}") begin bootstrap_config = @file_manager.read_bootstrap_configurations_from_file(@bootstrap_file) configurations = extract_configurations(JSON.parse(bootstrap_config, symbolize_names: true), @environment_id, @collection_id) load_configurations_to_cache(configurations) # TODO: emit(APPCONFIGURATION_CLIENT_EMITTER) unless @live_update rescue StandardError => e report_error(e) unless @live_update @logger.error(e..to_s) error_reading_bootstrap_config = true end end end # Implement live update logic return unless @live_update @logger.info("Live update enabled - fetching configurations from API...") # Track whether to start background retry start_background_retry = false # Create config fetcher instance config_fetcher = ConfigFetcher.new( collection_id: @collection_id, environment_id: @environment_id, logger: @logger ) # Fetch configurations from API fetch_result = config_fetcher.fetch if fetch_result[:ok] @logger.info("✅ Successfully fetched configurations from API") # Display raw API response # @logger.info("=" * 80) # @logger.info("📡 RAW API RESPONSE:") # @logger.info("-" * 80) require "json" # @logger.info(JSON.pretty_generate(fetch_result[:data])) # @logger.info("=" * 80) # Process and load configurations begin # Convert string keys to symbol keys symbolized_data = symbolize_keys(fetch_result[:data]) @logger.info("🔍 Symbolized data keys: #{symbolized_data.keys.inspect}") @logger.info("🔍 Environments: #{symbolized_data[:environments]&.length || 0}") @logger.info("🔍 Collections: #{symbolized_data[:collections]&.length || 0}") # Extract configurations using utils.rb method @logger.info("🔍 About to call extract_configurations") @logger.info(" Environment ID: #{@environment_id}") @logger.info(" Collection ID: #{@collection_id}") @logger.info(" Symbolized data has #{symbolized_data[:environments]&.first&.dig(:features)&.length || 0} features in first environment") extracted_config = extract_configurations( symbolized_data, @environment_id, @collection_id ) @logger.info("📊 Extracted config - Features: #{extracted_config[:features]&.length || 0}, Properties: #{extracted_config[:properties]&.length || 0}, Segments: #{extracted_config[:segments]&.length || 0}") if extracted_config[:features] && extracted_config[:features].empty? @logger.warning("⚠️ WARNING: 0 features extracted but API returned features!") @logger.warning(" This suggests an issue in extract_configurations or validate_resource") end # Load to cache using existing method load_configurations_to_cache(extracted_config) @logger.info("📊 Loaded to cache - Features: #{@feature_map.length}, Properties: #{@property_map.length}, Segments: #{@segment_map.length}") # Write to persistent storage if configured if @persistent_cache_directory formatted_config = format_config(extracted_config, @environment_id, @collection_id) write_to_persistent_storage(formatted_config) end @logger.info("✅ Configurations loaded successfully") rescue StandardError => e @logger.error("❌ Failed to process configurations: #{e.}") @logger.error(e.backtrace.first(3).join("\n")) end else # Failed to fetch from API status_code = fetch_result[:status] err_msg = "Status code: #{status_code}. Message: Failed to fetch the configurations from remote server." # Check for client-side errors (4xx except 429) report_error(err_msg) if status_code >= 400 && status_code < 500 && status_code != 429 # Check if we have fallback configurations (persistent cache or bootstrap) if persistent_cache_read = "Loaded the configurations from the persistent cache into the application." @logger.info("#{err_msg} #{}") start_background_retry = true # TODO: emit event elsif @bootstrap_file && !error_reading_bootstrap_config = "Loaded the configurations from the bootstrap file: #{@bootstrap_file} into the application." @logger.info("#{err_msg} #{}") start_background_retry = true # TODO: emit event else # No fallback available @logger.error("❌ No configurations available - neither from API nor from cache/bootstrap") report_error(err_msg) end end # Start WebSocket client for live updates @logger.info("🔌 Starting WebSocket client for live updates...") begin # Get required parameters from UrlBuilder url_builder = UrlBuilder.instance # Set @guid from url_builder if not already set @guid ||= url_builder.guid @websocket_client = WebSocketClient.new( region: url_builder.region, guid: @guid, apikey: url_builder.apikey, collection_id: @collection_id, environment_id: @environment_id, start_background_retry: start_background_retry ) @websocket_client.connect @logger.info("✅ WebSocket client started successfully") rescue StandardError => e @logger.error("❌ Failed to start WebSocket client: #{e.}") @logger.error(e.backtrace.first(3).join("\n")) end end |
#set_live_update(live_update) ⇒ Object
Set live update status
768 769 770 |
# File 'lib/ibm_appconfiguration_ruby_sdk/configurations/configuration_handler.rb', line 768 def set_live_update(live_update) @live_update = live_update end |
#set_persistent_cache_directory(directory) ⇒ Object
Set persistent cache directory
782 783 784 |
# File 'lib/ibm_appconfiguration_ruby_sdk/configurations/configuration_handler.rb', line 782 def set_persistent_cache_directory(directory) @persistent_cache_directory = directory end |
#track(event_key, entity_id) ⇒ Object
379 380 381 |
# File 'lib/ibm_appconfiguration_ruby_sdk/configurations/configuration_handler.rb', line 379 def track(event_key, entity_id) # TODO: Implement tracking logic end |
#write_to_persistent_storage(file_data) ⇒ Object
Write to persistent storage
154 155 156 157 158 159 160 |
# File 'lib/ibm_appconfiguration_ruby_sdk/configurations/configuration_handler.rb', line 154 def write_to_persistent_storage(file_data) return unless @persistent_cache_directory json = JSON.generate(file_data) file_path = File.join(@persistent_cache_directory, "appconfiguration.json") @file_manager.store_files(json, file_path) end |