lib/aliyun/rails/connector/roa_client.rb in aliyun-rails-0.1.9 vs lib/aliyun/rails/connector/roa_client.rb in aliyun-rails-0.1.10

- old
+ new

@@ -1,168 +1,168 @@ -# frozen_string_literal: true - -require "faraday" -require "securerandom" -require "active_support/all" -require "net/http" - -module Net::HTTPHeader - def capitalize(name) - name - end - private :capitalize -end - -module Aliyun - module Rails - module Connector - class ROAClient - attr_accessor :endpoint, :api_version, :access_key_id, - :access_key_secret, :security_token, :hostname, :opts - - def initialize(config) - 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] - end - - def request(method:, uri:, params: {}, body: {}, headers: {}, options: {}) - # :"Content-Type" => "application/json" to "content-type" => "application/json" - headers.deep_transform_keys! { |key| key.to_s.downcase } - mix_headers = default_headers.merge(headers) - - response = connection.send(method.downcase) do |request| - request.url uri, params - if body - if mix_headers["content-type"].start_with? "application/json" - request_body = body.to_json - elsif mix_headers["content-type"].start_with? "application/x-www-form-urlencoded" - request_body = URI.encode_www_form(body) - else - request_body = body - end - mix_headers["content-md5"] = Digest::MD5.base64digest request_body - mix_headers["content-length"] = request_body.length.to_s - request.body = request_body - end - string2sign = string_to_sign(method, uri, mix_headers, params) - mix_headers[:authorization] = authorization(string2sign) - mix_headers.each { |key, value| request.headers[key] = value } - end - - return response if options.has_key? :raw_body - - response_content_type = response.headers["Content-Type"] || "" - if response_content_type.start_with?("application/json") - if response.status >= 400 - result = JSON.parse(response.body) - raise StandardError, "code: #{response.status}, #{result['Message']} requestid: #{result['RequestId']}" - end - end - - if response_content_type.start_with?("text/xml") - result = Hash.from_xml(response.body) - raise ACSError, result["Error"] if result["Error"] - end - - response - end - - def connection(adapter = Faraday.default_adapter) - Faraday.new(url: self.endpoint) { |f| f.adapter adapter } - end - - def get(uri: "", headers: {}, params: {}, options: {}) - request(method: :get, uri: uri, params: params, body: {}, headers: headers, options: options) - end - - def post(uri: "", headers: {}, params: {}, body: {}, options: {}) - request(method: :post, uri: uri, params: params, body: body, headers: headers, options: options) - end - - def put(uri: "", headers: {}, params: {}, body: {}, options: {}) - request(method: :put, uri: uri, params: params, body: body, headers: headers, options: options) - end - - def delete(uri: "", headers: {}, params: {}, options: {}) - request(method: :delete, uri: uri, params: params, body: {}, headers: headers, options: options) - end - - def default_headers - default_headers = { - "accept" => "application/json", - "date" => Time.now.httpdate, - "host" => URI(self.endpoint).host, - "x-acs-signature-nonce" => SecureRandom.hex(16), - "x-acs-signature-method" => "HMAC-SHA1", - "x-acs-signature-version" => "1.0", - "x-acs-version" => self.api_version, - "x-sdk-client" => "RUBY(#{RUBY_VERSION})", # FIXME: 如何获取Gem的名称和版本号 - "user-agent" => DEFAULT_UA - } - if self.security_token - default_headers["x-acs-accesskey-id"] = self.access_key_id - default_headers["x-acs-security-token"] = self.security_token - end - default_headers - end - - private - def string_to_sign(method, uri, headers, query = {}) - header_string = [ - method, - headers["accept"], - headers["content-md5"] || "", - headers["content-type"] || "", - headers["date"], - ].join("\n") - "#{header_string}\n#{canonicalized_headers(headers)}#{canonicalized_resource(uri, query)}" - end - - def canonicalized_headers(headers) - headers.keys.select { |key| key.to_s.start_with? "x-acs-" } - .sort.map { |key| "#{key}:#{headers[key].strip}\n" }.join - end - - def canonicalized_resource(uri, query_hash = {}) - query_string = query_hash.sort.map { |key, value| "#{key}=#{value}" }.join("&") - query_string.empty? ? uri : "#{uri}?#{query_string}" - end - - def authorization(string_to_sign) - "acs #{self.access_key_id}:#{signature(string_to_sign)}" - end - - def signature(string_to_sign) - Base64.encode64(OpenSSL::HMAC.digest("sha1", self.access_key_secret, string_to_sign)).strip - end - - 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 - - class ACSError < StandardError - attr_accessor :code - - def initialize(error) - self.code = error["Code"] - message = error["Message"] - host_id = error["HostId"] - request_id = error["RequestId"] - super("#{message} host_id: #{host_id}, request_id: #{request_id}") - end - end - end - end - end -end +# frozen_string_literal: true + +require "faraday" +require "securerandom" +require "active_support/all" +require "net/http" + +module Net::HTTPHeader + def capitalize(name) + name + end + private :capitalize +end + +module Aliyun + module Rails + module Connector + class ROAClient + attr_accessor :endpoint, :api_version, :access_key_id, + :access_key_secret, :security_token, :hostname, :opts + + def initialize(config) + 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] + end + + def request(method:, uri:, params: {}, body: {}, headers: {}, options: {}) + # :"Content-Type" => "application/json" to "content-type" => "application/json" + headers.deep_transform_keys! { |key| key.to_s.downcase } + mix_headers = default_headers.merge(headers) + + response = connection.send(method.downcase) do |request| + request.url uri, params + if body + if mix_headers["content-type"].start_with? "application/json" + request_body = body.to_json + elsif mix_headers["content-type"].start_with? "application/x-www-form-urlencoded" + request_body = URI.encode_www_form(body) + else + request_body = body + end + mix_headers["content-md5"] = Digest::MD5.base64digest request_body + mix_headers["content-length"] = request_body.length.to_s + request.body = request_body + end + string2sign = string_to_sign(method, uri, mix_headers, params) + mix_headers[:authorization] = authorization(string2sign) + mix_headers.each { |key, value| request.headers[key] = value } + end + + return response if options.has_key? :raw_body + + response_content_type = response.headers["Content-Type"] || "" + if response_content_type.start_with?("application/json") + if response.status >= 400 + result = JSON.parse(response.body) + raise StandardError, "code: #{response.status}, #{result['Message']} requestid: #{result['RequestId']}" + end + end + + if response_content_type.start_with?("text/xml") + result = Hash.from_xml(response.body) + raise ACSError, result["Error"] if result["Error"] + end + + response + end + + def connection(adapter = Faraday.default_adapter) + Faraday.new(url: self.endpoint) { |f| f.adapter adapter } + end + + def get(uri: "", headers: {}, params: {}, options: {}) + request(method: :get, uri: uri, params: params, body: {}, headers: headers, options: options) + end + + def post(uri: "", headers: {}, params: {}, body: {}, options: {}) + request(method: :post, uri: uri, params: params, body: body, headers: headers, options: options) + end + + def put(uri: "", headers: {}, params: {}, body: {}, options: {}) + request(method: :put, uri: uri, params: params, body: body, headers: headers, options: options) + end + + def delete(uri: "", headers: {}, params: {}, options: {}) + request(method: :delete, uri: uri, params: params, body: {}, headers: headers, options: options) + end + + def default_headers + default_headers = { + "accept" => "application/json", + "date" => Time.now.httpdate, + "host" => URI(self.endpoint).host, + "x-acs-signature-nonce" => SecureRandom.hex(16), + "x-acs-signature-method" => "HMAC-SHA1", + "x-acs-signature-version" => "1.0", + "x-acs-version" => self.api_version, + "x-sdk-client" => "RUBY(#{RUBY_VERSION})", # FIXME: 如何获取Gem的名称和版本号 + "user-agent" => DEFAULT_UA + } + if self.security_token + default_headers["x-acs-accesskey-id"] = self.access_key_id + default_headers["x-acs-security-token"] = self.security_token + end + default_headers + end + + private + def string_to_sign(method, uri, headers, query = {}) + header_string = [ + method, + headers["accept"], + headers["content-md5"] || "", + headers["content-type"] || "", + headers["date"], + ].join("\n") + "#{header_string}\n#{canonicalized_headers(headers)}#{canonicalized_resource(uri, query)}" + end + + def canonicalized_headers(headers) + headers.keys.select { |key| key.to_s.start_with? "x-acs-" } + .sort.map { |key| "#{key}:#{headers[key].strip}\n" }.join + end + + def canonicalized_resource(uri, query_hash = {}) + query_string = query_hash.sort.map { |key, value| "#{key}=#{value}" }.join("&") + query_string.empty? ? uri : "#{uri}?#{query_string}" + end + + def authorization(string_to_sign) + "acs #{self.access_key_id}:#{signature(string_to_sign)}" + end + + def signature(string_to_sign) + Base64.encode64(OpenSSL::HMAC.digest("sha1", self.access_key_secret, string_to_sign)).strip + end + + 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 + + class ACSError < StandardError + attr_accessor :code + + def initialize(error) + self.code = error["Code"] + message = error["Message"] + host_id = error["HostId"] + request_id = error["RequestId"] + super("#{message} host_id: #{host_id}, request_id: #{request_id}") + end + end + end + end + end +end