lib/nucleus/adapter_resolver.rb
require 'base64'
module Nucleus
# The {AdapterResolver} can be used within Ruby applications to retrieve a Nucleus adapter.
# Returned adapters are patched so that each call enforces authentication and retries a call when a token was expired.
class AdapterResolver
include Nucleus::UrlConverter
def initialize(requested_version)
raise 'No such version supported' unless Nucleus::VersionDetector.api_versions.include?(requested_version)
@api_version = requested_version
end
# Get a list of all adapters that are currently supported.
# @return [Hash<String, Hash<String, Nucleus::Adapters::BaseAdapter>>] currently supported adapters
def adapters
setup
@adapters
end
# Load the adapter to interact with the platform of the vendor that is offered at the endpoint_url.
# @param [String] vendor The vendor / adapter name that shall be used to communicate with the endpoint.
# Must be provided, otherwise a +StandardError+ will be thrown.
# @param [String] username The username that shall be used for authentication
# @param [String] password The password that shall be used for authentication
# @param [Hash<Symbol,?>] options Further options to apply when creating the adapter instance.
# If available, the default configuration of the vendor configuration is applied as default.
# @option options [String] :app_domain The domain where applications of the platform will be made available at.
# This option must be set for custom deployments of platforms like Cloud Foundry or Openshift.
# For IBM Bluemix this value would be: +eu-gb.mybluemix.net+ or +ng.mybluemix.net+, depending on the endpoint.
# @option options [String] :check_ssl Set to false if SSL certificates shall not be verified (trust self-signed)
# @option options [String] :api_url URL of the endpoint's API that shall be used.
# Must be specified if there are multiple endpoints available and will be forced to https://
# @raise [StandardError] if the vendor is unknown / not supported or no unique API endpoint could be identified
# @return [Nucleus::Adapters::BaseAdapter] loaded adapter implementation
def load(vendor, username, password, options = {})
setup
raise StandardError, "Could not find adapter for vendor '#{vendor}'" unless @adapters.key?(vendor)
# load the endpoint's HTTPS enabled API URL
endpoint_url = load_endpoint(vendor, options)
# load default configuration if available
if @configurations[vendor].key?(endpoint_url)
default_configuration = @configurations[vendor][endpoint_url]
options = default_configuration.merge(options)
end
check_ssl = options.key?(:check_ssl) ? options[:check_ssl] : true
adapter = @adapters[vendor].new(endpoint_url, options[:app_domain], check_ssl)
fake_env = { 'HTTP_AUTHORIZATION' => 'Basic ' + Base64.strict_encode64("#{username}:#{password}") }
# patch the adapter so that calls are wrapped and expect valid authentication
AdapterAuthenticationInductor.patch(adapter, fake_env)
cache_key = adapter.cache_key(username, password)
# no auth object available, perform authentication first
auth_object = adapter.auth_client
# throws an error if the authentication failed
auth_object.authenticate(username, password)
# cache the auth object so it does not have to be retrieved per request
adapter.cache(cache_key, auth_object)
# return patched and initially authenticated adapter
adapter
end
private
def load_endpoint(vendor, options)
if options.key?(:api_url)
# use chosen url endpoint
endpoint_url = options[:api_url]
elsif @configurations[vendor].length == 1
# use default endpoint
endpoint_url = @configurations[vendor].keys.first
else
raise StandardError, "Could not identify an API endpoint for the vendor '#{vendor}'. "\
"Please specify the API URL to use with the ':api_url' option."
end
# make sure url uses https
secure_url(endpoint_url)
end
def setup
# perform the setup only once
return if @adapters
# Initialize the application (import adapters, load DAOs, ...)
require 'nucleus/scripts/initialize'
# load the configuration values
require 'nucleus/scripts/initialize_config_defaults'
# Once invoked the configuration is locked
require 'nucleus/scripts/finalize'
@adapters = {}
@configurations = {}
Nucleus::Adapters.configuration_files.each do |adapter_config|
vendor = Nucleus::VendorParser.parse(adapter_config)
next unless vendor
adapter_clazz = Nucleus::Adapters.adapter_clazz(adapter_config, @api_version)
next unless adapter_clazz
@adapters[vendor.id] = adapter_clazz
@configurations[vendor.id] = {}
# now load the default configurations for this vendor
vendor.providers.each do |provider|
provider.endpoints.each do |endpoint|
@configurations[vendor.id][secure_url(endpoint.url)] = { check_ssl: !endpoint.trust,
app_domain: endpoint.app_domain }
end
end
end
end
end
end