class Transip::Client
Constants
- API_SERVICE
- API_VERSION
Attributes
Public Class Methods
Options:
-
username - Your login name on the TransIP website.
-
ip - needed in production
-
key / key_file - key is one of your private keys (these can be requested via your Controlpanel). key_file is path to file containing key.
-
mode - :readonly, :readwrite
-
proxy - url of proxy through which you want to route API requests. For example, if you use Quataguard Static on Heroku, use ENV. If not used, leave blank or don't supply it as a parameter.
Example:
transip = Transip.new(:username => 'api_username', :ip => '12.34.12.3', :key => mykey, :mode => 'readwrite', :proxy => '') # use this in production
# File lib/transip/client.rb, line 35 def initialize(options = {}) @key = options[:key] || (options[:key_file] && File.read(options[:key_file])) @username = options[:username] @ip = options[:ip] @proxy = options[:proxy] @api_version = options[:api_version] @api_service = options[:api_service] raise ArgumentError, "The :username, :ip and :key options are required!" if @username.nil? or @key.nil? @mode = options[:mode] || :readonly @endpoint = options[:endpoint] || 'api.transip.nl' if options[:password] @password = options[:password] end @savon_options = { :wsdl => wsdl } # if proxy is present, use it if @proxy != nil @savon_options[:proxy] = @proxy end # By default we don't want to debug! self.turn_off_debugging! end
Public Instance Methods
Returns Array with all possible SOAP WSDL actions.
# File lib/transip/client.rb, line 202 def actions client.operations end
# File lib/transip/client.rb, line 16 def api_service @api_service || self.class::API_SERVICE end
# File lib/transip/client.rb, line 11 def api_version # We use self.class:: here to not use parentclass constant. @api_version || self.class::API_VERSION end
Returns a Savon::Client object to be used in the connection. This object is re-used and cached as @client.
# File lib/transip/client.rb, line 197 def client @client ||= client! end
Same as client method but initializes a brand new fresh client. You have to use this one when you want to re-set the mode (readwrite, readonly), or authentication details of your client.
# File lib/transip/client.rb, line 186 def client! @client = Savon::Client.new(@savon_options) do namespaces( "xmlns:enc" => "http://schemas.xmlsoap.org/soap/encoding/" ) end return @client end
yes, i know, it smells bad
# File lib/transip/client.rb, line 77 def convert_array_to_hash(array) result = {} array.each_with_index do |value, index| result[index] = value end result end
This makes sure that arrays are properly encoded as soap-arrays by Gyoku
# File lib/transip/client.rb, line 207 def fix_array_definitions(options) result = {} options.each do |key, value| if value.is_a?(Array) and (value.size > 0) entry_name = value.first.class.name.split(":").last result[key] = { 'item' => {:content! => value, :'@xsi:type' => "tns:#{entry_name}"}, :'@xsi:type' => "tns:ArrayOf#{entry_name}", :'@enc:arrayType' => "tns:#{entry_name}[#{value.size}]" } elsif value.is_a?(Hash) result[key] = fix_array_definitions(value) else result[key] = value end end result end
converts the savon response object to something we can return to the caller
-
A
TransipStruct
object -
An array of TransipStructs
-
nil
# File lib/transip/client.rb, line 230 def process_response(response) response = response.to_hash.values.first[:return] rescue nil TransipStruct.from_soap(response) end
This is the main request function throws ApiError
returns response object (can be TransipStruct
or Array of TransipStruct
)
# File lib/transip/client.rb, line 238 def request(action, options = nil) formatted_action = action.to_s.camelize(:lower) parameters = { # for some reason, the transip server wants the body root tag to be # the name of the action. :message_tag => formatted_action } options = options.to_hash if options.is_a?(Transip::TransipStruct) if options.is_a?(Hash) xml_options = fix_array_definitions(options) elsif options.nil? xml_options = nil else raise "Invalid parameter format (should be nil, hash or TransipStruct)" end parameters[:message] = xml_options parameters[:cookies] = cookies(action, options) debug_log("parameters:\n#{parameters.inspect}") response = client.call(action, parameters) process_response(response) rescue Savon::SOAPFault => e raise ApiError.new(e), e.message.sub(/^\(\d+\)\s+/,'') # We raise our own error (FIXME: Correct?). end
# File lib/transip/client.rb, line 93 def serialize_parameters(parameters, key_prefix=nil) debug_log("serialize_parameters(#{parameters.inspect}, #{key_prefix.inspect}") parameters = parameters.to_hash.values.first if parameters.is_a? TransipStruct parameters = convert_array_to_hash(parameters) if parameters.is_a? Array if not parameters.is_a? Hash return urlencode(parameters) end return "#{key_prefix}=" if parameters.empty? encoded_parameters = [] parameters.each do |key, value| next if key.to_s == '@xsi:type' encoded_key = (key_prefix.nil?) ? urlencode(key) : "#{key_prefix}[#{urlencode(key)}]" if value.is_a?(Hash) or value.is_a?(Array) or value.is_a?(TransipStruct) encoded_parameters << serialize_parameters(value, encoded_key) else encoded_value = urlencode(value) encoded_parameters << "#{encoded_key}=#{encoded_value}" end end encoded_parameters = encoded_parameters.join("&") debug_log("encoded_parameters:\n#{encoded_parameters.split('&').join("\n")}") encoded_parameters end
does all the techy stuff to calculate transip's sick authentication scheme: a hash with all the request information is subsequently: serialized like a www form SHA512 digested asn1 header added private key encrypted Base64 encoded URL encoded I think the guys at transip were trying to use their entire crypto-toolbox!
# File lib/transip/client.rb, line 129 def signature(method, parameters, time, nonce) formatted_method = method.to_s.camelize(:lower) parameters ||= {} input = convert_array_to_hash(parameters.values) options = { '__method' => formatted_method, '__service' => api_service, '__hostname' => @endpoint, '__timestamp' => time, '__nonce' => nonce } input.merge!(options) raise "Invalid RSA key" unless @key =~ /-----BEGIN (RSA )?PRIVATE KEY-----(.*)-----END (RSA )?PRIVATE KEY-----/sim serialized_input = serialize_parameters(input) digest = Digest::SHA512.new.digest(serialized_input) asn_header = "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40" # convert asn_header literal to ASCII-8BIT if RUBY_VERSION.split('.')[0] == "2" asn = asn_header.b + digest else asn = asn_header + digest end private_key = OpenSSL::PKey::RSA.new(@key) encrypted_asn = private_key.private_encrypt(asn) readable_encrypted_asn = Base64.encode64(encrypted_asn) urlencode(readable_encrypted_asn) end
By default we don't want to debug! Changing might impact other Savon usages.
# File lib/transip/client.rb, line 63 def turn_off_debugging! @savon_options[:log] = false # disable logging @savon_options[:log_level] = :info # changing the log level end
# File lib/transip/client.rb, line 85 def urlencode(input) output = URI.encode_www_form_component(input) output.gsub!('+', '%20') output.gsub!('%7E', '~') output.gsub!('*', '%2A') output end
Make Savon log to Rails.logger and turn_off_debugging!
# File lib/transip/client.rb, line 69 def use_with_rails! if Rails.env.production? self.turn_off_debugging! end @savon_options[:logger] = Rails.logger # using the Rails logger end
# File lib/transip/client.rb, line 20 def wsdl "https://api.transip.nl/wsdl/?service=#{api_service}" end
Private Instance Methods
# File lib/transip/client.rb, line 266 def debug_log(msg) puts msg if @debug end