class AliyunSDKCore::RPCClient

Attributes

access_key_id[RW]
access_key_secret[RW]
api_version[RW]
codes[RW]
endpoint[RW]
opts[RW]
security_token[RW]
verbose[RW]

Public Class Methods

new(config, verbose = false) click to toggle source
# File lib/aliyunsdkcore/rpc_client.rb, line 12
def initialize(config, verbose = false)

  validate config

  self.endpoint          = config[:endpoint]
  self.api_version       = config[:api_version]
  self.access_key_id     = config[:access_key_id]
  self.access_key_secret = config[:access_key_secret]
  self.security_token    = config[:security_token]
  self.opts              = config[:opts] || {}
  self.verbose           = verbose.instance_of?(TrueClass) && verbose
  self.codes             = Set.new [200, '200', 'OK', 'Success']
  self.codes.merge config[:codes] if config[:codes]
end

Public Instance Methods

request(action:, params: {}, opts: {}) click to toggle source
# File lib/aliyunsdkcore/rpc_client.rb, line 27
def request(action:, params: {}, opts: {})
  opts           = self.opts.merge(opts)
  action         = upcase_first(action) if opts[:format_action]
  params         = format_params(params) unless opts[:format_params]
  defaults       = default_params
  params         = { Action: action }.merge(defaults).merge(params)
  method         = (opts[:method] || 'GET').upcase
  normalized     = normalize(params)
  canonicalized  = canonicalize(normalized)
  string_to_sign = "#{method}&#{encode('/')}&#{encode(canonicalized)}"
  key            = self.access_key_secret + '&'
  signature      = Base64.encode64(OpenSSL::HMAC.digest('sha1', key, string_to_sign)).strip
  normalized.push(['Signature', encode(signature)])

  querystring = canonicalize(normalized)

  uri           = opts[:method] == 'POST' ? '/' : "/?#{querystring}"

  response      = connection.send(method.downcase, uri) do |request|
    request.headers['User-Agent'] = DEFAULT_UA
    if opts[:method] == 'POST'
      request.headers['Content-Type'] = 'application/x-www-form-urlencoded'
      request.body                    = querystring
    end
  end

  response_body = JSON.parse(response.body)
  if response_body['Code'] && !response_body['Code'].to_s.empty? && !self.codes.include?(response_body['Code'])
    raise StandardError, "Code: #{response_body['Code']}, Message: #{response_body['Message']}, URL: #{uri}"
  end

  response_body
end

Private Instance Methods

canonicalize(normalized) click to toggle source
# File lib/aliyunsdkcore/rpc_client.rb, line 131
def canonicalize(normalized)
  normalized.map { |element| "#{element.first}=#{element.last}" }.join('&')
end
connection(adapter = Faraday.default_adapter) click to toggle source
# File lib/aliyunsdkcore/rpc_client.rb, line 72
def connection(adapter = Faraday.default_adapter)
  Faraday.new(:url => self.endpoint) { |faraday| faraday.adapter adapter }
end
default_params() click to toggle source
# File lib/aliyunsdkcore/rpc_client.rb, line 76
def default_params
  default_params = {
    'Format'           => 'JSON',
    'SignatureMethod'  => 'HMAC-SHA1',
    'SignatureNonce'   => SecureRandom.hex(16),
    'SignatureVersion' => '1.0',
    'Timestamp'        => Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ'),
    'AccessKeyId'      => self.access_key_id,
    'Version'          => self.api_version,
  }
  default_params.merge!('SecurityToken' => self.security_token) if self.security_token
  default_params
end
encode(string) click to toggle source
# File lib/aliyunsdkcore/rpc_client.rb, line 90
def encode(string)
  string = string.to_s unless string.is_a?(String)
  encoded = CGI.escape string
  encoded.gsub(/[\+]/, '%20')
end
flat_params(params) click to toggle source
# File lib/aliyunsdkcore/rpc_client.rb, line 112
def flat_params(params)
  target = {}
  params.each do |key, value|
    if value.instance_of?(Array)
      replace_repeat_list(target, key, value)
    else
      target[key.to_s] = value
    end
  end
  target
end
format_params(param_hash) click to toggle source
# File lib/aliyunsdkcore/rpc_client.rb, line 96
def format_params(param_hash)
  param_hash.keys.each { |key| param_hash[upcase_first(key.to_s).to_sym] = param_hash.delete key }
  param_hash
end
normalize(params) click to toggle source
# File lib/aliyunsdkcore/rpc_client.rb, line 124
def normalize(params)
  flat_params(params)
    .sort
    .to_h
    .map { |key, value| [encode(key), encode(value)] }
end
replace_repeat_list(target, key, repeat) click to toggle source
# File lib/aliyunsdkcore/rpc_client.rb, line 101
def replace_repeat_list(target, key, repeat)
  repeat.each_with_index do |item, index|
    if item && item.instance_of?(Hash)
      item.each_key { |k| target["#{key}.#{index.next}.#{k}"] = item[k] }
    else
      target["#{key}.#{index.next}"] = item
    end
  end
  target
end
upcase_first(string) click to toggle source

Converts just the first character to uppercase.

upcase_first('what a Lovely Day') # => "What a Lovely Day"
upcase_first('w')                 # => "W"
upcase_first('')                  # => ""
# File lib/aliyunsdkcore/rpc_client.rb, line 68
def upcase_first(string)
  string.length > 0 ? string[0].upcase.concat(string[1..-1]) : ""
end
validate(config) click to toggle source
# File lib/aliyunsdkcore/rpc_client.rb, line 135
def validate(config)
  raise ArgumentError, 'must pass "config"' unless config
  raise ArgumentError, 'must pass "config[:endpoint]"' unless config[:endpoint]
  unless config[:endpoint].start_with?('http://') || config[:endpoint].start_with?('https://')
    raise ArgumentError, '"config.endpoint" must starts with \'https://\' or \'http://\'.'
  end
  raise ArgumentError, 'must pass "config[:api_version]"' unless config[:api_version]
  raise ArgumentError, 'must pass "config[:access_key_id]"' unless config[:access_key_id]
  raise ArgumentError, 'must pass "config[:access_key_secret]"' unless config[:access_key_secret]
end