Class: IbmAppconfigurationRubySdk::ApiManager
- Inherits:
-
Object
- Object
- IbmAppconfigurationRubySdk::ApiManager
- Defined in:
- lib/ibm_appconfiguration_ruby_sdk/api_manager.rb
Constant Summary collapse
- SDK_VERSION =
SDK version for User-Agent header
IbmAppconfigurationRubySdk::VERSION
Class Attribute Summary collapse
-
.iam_authenticator ⇒ IBMCloudSdkCore::IamAuthenticator?
readonly
Get the IAM Authenticator instance.
Class Method Summary collapse
-
.base_service_client ⇒ IBMCloudSdkCore::BaseService
Get the BaseService client with retry configuration.
-
.headers(is_post = false) ⇒ Hash
Get the request headers for API calls.
-
.inspect ⇒ String
Returns a developer-friendly string that hides authenticator internals.
-
.post_metering(url, metering_data, _apikey) ⇒ IBMCloudSdkCore::DetailedResponse
Post metering data to the App Configuration service.
-
.refresh_token! ⇒ void
Force a full token refresh by recreating the IamAuthenticator.
-
.reset! ⇒ void
Reset the ApiManager state (useful for testing).
-
.set_authenticator ⇒ void
Sets the IAM Authenticator using the API key from UrlBuilder.
-
.token ⇒ String
Get the IAM bearer token for WebSocket authentication.
Class Attribute Details
.iam_authenticator ⇒ IBMCloudSdkCore::IamAuthenticator? (readonly)
Get the IAM Authenticator instance
303 304 305 |
# File 'lib/ibm_appconfiguration_ruby_sdk/api_manager.rb', line 303 def iam_authenticator @iam_authenticator end |
Class Method Details
.base_service_client ⇒ IBMCloudSdkCore::BaseService
Get the BaseService client with retry configuration
Creates a new BaseService client if one doesn't exist, configured with:
- The IAM authenticator
- Retry logic (max 3 retries with exponential backoff)
- Base service URL from UrlBuilder
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/ibm_appconfiguration_ruby_sdk/api_manager.rb', line 150 def base_service_client if @base_service_client.nil? raise IbmAppconfigurationRubySdk::ConfigurationError, "Authenticator not set. Call set_authenticator first." if @iam_authenticator.nil? @url_builder ||= UrlBuilder.instance @base_service_client = IBMCloudSdkCore::BaseService.new( service_name: "app_configuration", authenticator: @iam_authenticator, service_url: @url_builder.base_service_url ) # Configure retry settings # Note: Ruby SDK Core v1.3.0 uses configure_http_client for retry settings @base_service_client.configure_http_client( timeout: { connect: 60, read: 60, write: 60 } ) end @base_service_client end |
.headers(is_post = false) ⇒ Hash
Get the request headers for API calls
69 70 71 72 73 74 75 76 |
# File 'lib/ibm_appconfiguration_ruby_sdk/api_manager.rb', line 69 def headers(is_post = false) headers = { "Accept" => "application/json", "User-Agent" => "appconfiguration-ruby-sdk/#{SDK_VERSION}" } headers["Content-Type"] = "application/json" if is_post headers end |
.inspect ⇒ String
Returns a developer-friendly string that hides authenticator internals. Prevents accidental credential exposure when the class is printed.
327 328 329 |
# File 'lib/ibm_appconfiguration_ruby_sdk/api_manager.rb', line 327 def inspect "#<#{self} authenticated=#{!@iam_authenticator.nil?}>" end |
.post_metering(url, metering_data, _apikey) ⇒ IBMCloudSdkCore::DetailedResponse
Post metering data to the App Configuration service
Sends usage metrics for feature and property evaluations to the billing server.
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 |
# File 'lib/ibm_appconfiguration_ruby_sdk/api_manager.rb', line 275 def post_metering(url, metering_data, _apikey) # Force the IAM token manager to check expiry and refresh before every # metering POST. Without this, long-running processes reuse a cached token # that the metering endpoint rejects with 401 once it expires (~1 hour). refresh_token! # Extract the path from the full URL uri = URI.parse(url) path = uri.path # The IBM Cloud Ruby SDK's BaseService.request method signature: # request(method:, url:, headers: nil, params: nil, json: nil, data: nil) # For POST with JSON body, we should use the 'json' parameter (not 'body') client = base_service_client client.request( method: "POST", url: path, headers: headers(true), json: metering_data # Use 'json' parameter for JSON body ) end |
.refresh_token! ⇒ void
This method returns an undefined value.
Force a full token refresh by recreating the IamAuthenticator.
--- Why patching the existing token manager does NOT reliably work ---
The Node SDK uses createRequest() which calls tokenManager.getToken()
on EVERY request — that method checks expiry and fetches a new token when
needed. The Ruby SDK's BaseService#request calls authenticate() which
calls token_manager.access_token — a raw hash read with NO expiry check.
We tried calling token_manager.token (the method with the expiry check)
before each request, but token_expired? uses an 80%-of-TTL threshold
computed from the JWT's exp and iat fields. If the IAM server issues a
token that the metering endpoint rejects before the 80% threshold is
reached (e.g. the test environment uses shorter-lived metering grants, or
the token is valid for config/websocket but not for the metering scope),
token_expired? returns false and no refresh happens.
--- The correct fix ---
Recreate the IamAuthenticator entirely. Its initialize immediately calls
IAMTokenManager#token → request_token, which unconditionally fetches a
brand-new token from IAM. We also nil out @base_service_client so it is
rebuilt with the new authenticator, exactly matching the Node SDK pattern
where getBaseServiceClient() wires up a fresh authenticator on each call.
This is safe to call from the metering thread because:
set_authenticatoris idempotent (replaces ivars atomically in MRI due to GVL)base_service_clientrecreates lazily on next use- WebSocket uses
tokenwhich callsauthenticateon the NEW authenticator
247 248 249 250 251 252 253 254 |
# File 'lib/ibm_appconfiguration_ruby_sdk/api_manager.rb', line 247 def refresh_token! raise IbmAppconfigurationRubySdk::ConfigurationError, "Authenticator not set. Call set_authenticator first." if @iam_authenticator.nil? # Recreate the authenticator — this unconditionally fetches a fresh IAM token. # Also clear the cached BaseService so it is rebuilt with the new authenticator. @base_service_client = nil set_authenticator end |
.reset! ⇒ void
This method returns an undefined value.
Reset the ApiManager state (useful for testing)
Clears all cached instances, forcing re-initialization on next use.
316 317 318 319 320 |
# File 'lib/ibm_appconfiguration_ruby_sdk/api_manager.rb', line 316 def reset! @iam_authenticator = nil @base_service_client = nil @url_builder = nil end |
.set_authenticator ⇒ void
This method returns an undefined value.
Sets the IAM Authenticator using the API key from UrlBuilder
This method initializes the IBM Cloud IAM authenticator with the API key and IAM URL configured in the UrlBuilder singleton.
93 94 95 96 97 98 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 |
# File 'lib/ibm_appconfiguration_ruby_sdk/api_manager.rb', line 93 def set_authenticator @url_builder = UrlBuilder.instance # Create authenticator with apikey and optional URL = { apikey: @url_builder.apikey } # Add URL if it's not the default production URL # Check for test/staging environment (iam.test.cloud.ibm.com) or custom URLs iam_url = @url_builder.iam_url default_prod_url = "#{UrlBuilder::HTTPS_PROTOCOL}#{UrlBuilder::IAM_PROD_URL}" if iam_url && iam_url != default_prod_url [:url] = iam_url @logger.log("Using custom IAM URL: #{iam_url}") else @logger.log("Using default IAM URL: #{default_prod_url}") end # IBMCloudSdkCore::IamAuthenticator#initialize immediately fetches a token, # so a bad API key raises IBMCloudSdkCore::ApiException here rather than # at first use. Wrap it so callers only ever see our typed error hierarchy. begin @iam_authenticator = IBMCloudSdkCore::IamAuthenticator.new() rescue IBMCloudSdkCore::ApiException => e status = e.code.to_i = e.error || e. raise IbmAppconfigurationRubySdk::APIError.from_status( status.positive? ? status : 401, message: "IAM authentication failed: #{}" ) rescue StandardError => e raise IbmAppconfigurationRubySdk::ConfigurationError, "Failed to initialise IAM authenticator: #{e.}" end end |
.token ⇒ String
Get the IAM bearer token for WebSocket authentication
This method authenticates with IAM and retrieves the bearer token that can be used for WebSocket connections.
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 |
# File 'lib/ibm_appconfiguration_ruby_sdk/api_manager.rb', line 188 def token raise IbmAppconfigurationRubySdk::ConfigurationError, "Authenticator not set. Call set_authenticator first." if @iam_authenticator.nil? # Create an empty request hash - the SDK will populate it request = {} # Force token refresh by setting force_refresh option # This ensures we get a fresh token, especially important for reconnections # The IBM Cloud SDK Core will check token expiration and refresh if needed refresh_token! @iam_authenticator.authenticate(request) # The Ruby SDK puts the Authorization header directly in the request hash # Try both string and symbol keys for compatibility = request["Authorization"] || request[:Authorization] raise IbmAppconfigurationRubySdk::ConfigurationError, "Authentication succeeded but no Authorization header was set. Request: #{request.inspect}" if .nil? # Log token info for debugging (first 20 chars only for security) token_preview = [0..19] if @logger.log("#{Constants::IAM_TOKEN_OBTAINED}: #{token_preview}...") end |