module Nucleus # The {Adapters} module combines all application logic to communicate with the different vendor platforms # and created the unified API. module Adapters # The {BaseAdapter} is an abstract class that shall be extended by all actual Adapters. # It provides methods to common functionality:
# * authentication (+cache) # * http client with general error handling # * native platform API calls # @abstract class BaseAdapter include HttpClient include HttpTailClient include Logging attr_reader :endpoint_url def initialize(endpoint_url, endpoint_app_domain = nil, check_certificates = true) raise ArgumentError, "'endpoint_url' must be a valid URL" unless endpoint_url =~ /\A#{URI.regexp(['https'])}\z/ @endpoint_url = endpoint_url @endpoint_app_domain = endpoint_app_domain @check_certificates = check_certificates end # thread-based cache for the api authorization headers thread_config_accessor :auth_objects_cache, default: {} # Cache the auth information. # @param [String] key cache key # @param [Nucleus::Adapters::AuthClient] auth_object authentication client to be cached # @return [void] def cache(key, auth_object) auth_objects_cache[key] = auth_object end # Are there cached information for this key? # @param [String] key cache key # @return [true, false] true if has cached auth info, else false def cache?(key) auth_objects_cache.key? key end # Get the currently cached authentication object. # @param [String] key cache key # @return [Hash, Nucleus::Adapters::AuthClient] cached authentication client def cached(key) return nil unless cache?(key) auth_objects_cache[key] end # Create the cache key for the username / password combination and save it in the {::RequestStore} to make it # available throughout the current request. # @param [String] username the username for the authentication # @param [String] password the password for the authentication # @return [String] calculated hash key for the input values def cache_key(username, password) # calculate the cache only once per request return RequestStore.store[:cache_key] if RequestStore.exist?(:cache_key) key = Digest::SHA256.hexdigest "#{endpoint_url}#{username}:#{password}" RequestStore.store[:cache_key] = key key end # Get the cached authentication object and retrieve the presumably valid authentication header. # @return [Hash] hash including a valid authentication header def headers auth_object = auth_objects_cache[RequestStore.store[:cache_key]] # AuthClient, generates the header for us auth_object.auth_header end # Execute an API call, targeted directly against the vendors API. # @param [Symbol] method http method to use, one of: [:GET, :POST, :DELETE, :PUT, :PATCH] # @param [String] path url path to append to the endpoint's URL # @param [Hash] params body params to use for PATCH, :PUT and :POST requests # @return [Object] the actual response body of the vendor platform def endpoint_call(method, path, params) case method when :GET get(path, native_call: true).body when :POST post(path, native_call: true, body: params).body when :DELETE delete(path, native_call: true).body when :PUT put(path, native_call: true, body: params).body when :PATCH patch(path, native_call: true, body: params).body else raise AdapterRequestError, 'Unsupported adapter call method. Allowed are: GET, POST, PATCH, PUT, DELETE' end end # Fail with a {Errors::PlatformSpecificSemanticError} error and format the error message to include the values # that are passed in the params. Requires the adapter to provide a +semantic_error_messages+ method, which shall # return a Hash with the platform specific semantic errors. # @param [Symbol] error_name error that shall be returned # @param [Array] params values that are to be included in the error message template # @raise [Errors::PlatformSpecificSemanticError] def fail_with(error_name, params = nil) unless respond_to?(:semantic_error_messages) raise StandardError 'Invalid adapter implementation, no :semantic_error_messages method provided' end error = semantic_error_messages[error_name] raise Errors::PlatformSpecificSemanticError.new(error[:message] % params, error[:code]) end end end end