aliyun-rails 0.1.17 → 0.1.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -15
- data/CODE_OF_CONDUCT.md +84 -84
- data/CONTRIBUTING.md +1 -0
- data/LICENSE.txt +21 -21
- data/README.md +152 -152
- data/Rakefile +8 -8
- data/lib/aliyun/connector/roa_client.rb +166 -166
- data/lib/aliyun/connector/rpc_client.rb +148 -116
- data/lib/aliyun/dysms.rb +25 -25
- data/lib/aliyun/dyvms.rb +25 -25
- data/lib/aliyun/version.rb +7 -7
- data/lib/aliyun-rails.rb +25 -26
- metadata +7 -6
@@ -1,166 +1,166 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "faraday"
|
4
|
-
require "securerandom"
|
5
|
-
require "active_support/all"
|
6
|
-
require "net/http"
|
7
|
-
|
8
|
-
module Net::HTTPHeader
|
9
|
-
def capitalize(name)
|
10
|
-
name
|
11
|
-
end
|
12
|
-
private :capitalize
|
13
|
-
end
|
14
|
-
|
15
|
-
module Aliyun
|
16
|
-
module Connector
|
17
|
-
class ROAClient
|
18
|
-
attr_accessor :endpoint, :api_version, :access_key_id,
|
19
|
-
:access_key_secret, :security_token, :hostname, :opts
|
20
|
-
|
21
|
-
def initialize(config)
|
22
|
-
validate config
|
23
|
-
|
24
|
-
self.endpoint = config[:endpoint]
|
25
|
-
self.api_version = config[:api_version]
|
26
|
-
self.access_key_id = config[:access_key_id]
|
27
|
-
self.access_key_secret = config[:access_key_secret]
|
28
|
-
self.security_token = config[:security_token]
|
29
|
-
end
|
30
|
-
|
31
|
-
def request(method:, uri:, params: {}, body: {}, headers: {}, options: {})
|
32
|
-
# :"Content-Type" => "application/json" to "content-type" => "application/json"
|
33
|
-
headers.deep_transform_keys! { |key| key.to_s.downcase }
|
34
|
-
mix_headers = default_headers.merge(headers)
|
35
|
-
|
36
|
-
response = connection.send(method.downcase) do |request|
|
37
|
-
request.url uri, params
|
38
|
-
if body
|
39
|
-
if mix_headers["content-type"].start_with? "application/json"
|
40
|
-
request_body = body.to_json
|
41
|
-
elsif mix_headers["content-type"].start_with? "application/x-www-form-urlencoded"
|
42
|
-
request_body = URI.encode_www_form(body)
|
43
|
-
else
|
44
|
-
request_body = body
|
45
|
-
end
|
46
|
-
mix_headers["content-md5"] = Digest::MD5.base64digest request_body
|
47
|
-
mix_headers["content-length"] = request_body.length.to_s
|
48
|
-
request.body = request_body
|
49
|
-
end
|
50
|
-
string2sign = string_to_sign(method, uri, mix_headers, params)
|
51
|
-
mix_headers[:authorization] = authorization(string2sign)
|
52
|
-
mix_headers.each { |key, value| request.headers[key] = value }
|
53
|
-
end
|
54
|
-
|
55
|
-
return response if options.has_key? :raw_body
|
56
|
-
|
57
|
-
response_content_type = response.headers["Content-Type"] || ""
|
58
|
-
if response_content_type.start_with?("application/json")
|
59
|
-
if response.status >= 400
|
60
|
-
result = JSON.parse(response.body)
|
61
|
-
raise StandardError, "code: #{response.status}, #{result['Message']} requestid: #{result['RequestId']}"
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
if response_content_type.start_with?("text/xml")
|
66
|
-
result = Hash.from_xml(response.body)
|
67
|
-
raise ACSError, result["Error"] if result["Error"]
|
68
|
-
end
|
69
|
-
|
70
|
-
response
|
71
|
-
end
|
72
|
-
|
73
|
-
def connection(adapter = Faraday.default_adapter)
|
74
|
-
Faraday.new(url: self.endpoint) { |f| f.adapter adapter }
|
75
|
-
end
|
76
|
-
|
77
|
-
def get(uri: "", headers: {}, params: {}, options: {})
|
78
|
-
request(method: :get, uri: uri, params: params, body: {}, headers: headers, options: options)
|
79
|
-
end
|
80
|
-
|
81
|
-
def post(uri: "", headers: {}, params: {}, body: {}, options: {})
|
82
|
-
request(method: :post, uri: uri, params: params, body: body, headers: headers, options: options)
|
83
|
-
end
|
84
|
-
|
85
|
-
def put(uri: "", headers: {}, params: {}, body: {}, options: {})
|
86
|
-
request(method: :put, uri: uri, params: params, body: body, headers: headers, options: options)
|
87
|
-
end
|
88
|
-
|
89
|
-
def delete(uri: "", headers: {}, params: {}, options: {})
|
90
|
-
request(method: :delete, uri: uri, params: params, body: {}, headers: headers, options: options)
|
91
|
-
end
|
92
|
-
|
93
|
-
def default_headers
|
94
|
-
default_headers = {
|
95
|
-
"accept" => "application/json",
|
96
|
-
"date" => Time.now.httpdate,
|
97
|
-
"host" => URI(self.endpoint).host,
|
98
|
-
"x-acs-signature-nonce" => SecureRandom.hex(16),
|
99
|
-
"x-acs-signature-method" => "HMAC-SHA1",
|
100
|
-
"x-acs-signature-version" => "1.0",
|
101
|
-
"x-acs-version" => self.api_version,
|
102
|
-
"x-sdk-client" => "RUBY(#{RUBY_VERSION})", # FIXME: 如何获取Gem的名称和版本号
|
103
|
-
"user-agent" => DEFAULT_UA
|
104
|
-
}
|
105
|
-
if self.security_token
|
106
|
-
default_headers["x-acs-accesskey-id"] = self.access_key_id
|
107
|
-
default_headers["x-acs-security-token"] = self.security_token
|
108
|
-
end
|
109
|
-
default_headers
|
110
|
-
end
|
111
|
-
|
112
|
-
private
|
113
|
-
def string_to_sign(method, uri, headers, query = {})
|
114
|
-
header_string = [
|
115
|
-
method,
|
116
|
-
headers["accept"],
|
117
|
-
headers["content-md5"] || "",
|
118
|
-
headers["content-type"] || "",
|
119
|
-
headers["date"],
|
120
|
-
].join("\n")
|
121
|
-
"#{header_string}\n#{canonicalized_headers(headers)}#{canonicalized_resource(uri, query)}"
|
122
|
-
end
|
123
|
-
|
124
|
-
def canonicalized_headers(headers)
|
125
|
-
headers.keys.select { |key| key.to_s.start_with? "x-acs-" }
|
126
|
-
.sort.map { |key| "#{key}:#{headers[key].strip}\n" }.join
|
127
|
-
end
|
128
|
-
|
129
|
-
def canonicalized_resource(uri, query_hash = {})
|
130
|
-
query_string = query_hash.sort.map { |key, value| "#{key}=#{value}" }.join("&")
|
131
|
-
query_string.empty? ? uri : "#{uri}?#{query_string}"
|
132
|
-
end
|
133
|
-
|
134
|
-
def authorization(string_to_sign)
|
135
|
-
"acs #{self.access_key_id}:#{signature(string_to_sign)}"
|
136
|
-
end
|
137
|
-
|
138
|
-
def signature(string_to_sign)
|
139
|
-
Base64.encode64(OpenSSL::HMAC.digest("sha1", self.access_key_secret, string_to_sign)).strip
|
140
|
-
end
|
141
|
-
|
142
|
-
def validate(config)
|
143
|
-
raise ArgumentError, 'must pass "config"' unless config
|
144
|
-
raise ArgumentError, 'must pass "config[:endpoint]"' unless config[:endpoint]
|
145
|
-
unless config[:endpoint].start_with?("http://") || config[:endpoint].start_with?("https://")
|
146
|
-
raise ArgumentError, '"config.endpoint" must starts with \'https://\' or \'http://\'.'
|
147
|
-
end
|
148
|
-
raise ArgumentError, 'must pass "config[:api_version]"' unless config[:api_version]
|
149
|
-
raise ArgumentError, 'must pass "config[:access_key_id]"' unless config[:access_key_id]
|
150
|
-
raise ArgumentError, 'must pass "config[:access_key_secret]"' unless config[:access_key_secret]
|
151
|
-
end
|
152
|
-
|
153
|
-
class ACSError < StandardError
|
154
|
-
attr_accessor :code
|
155
|
-
|
156
|
-
def initialize(error)
|
157
|
-
self.code = error["Code"]
|
158
|
-
message = error["Message"]
|
159
|
-
host_id = error["HostId"]
|
160
|
-
request_id = error["RequestId"]
|
161
|
-
super("#{message} host_id: #{host_id}, request_id: #{request_id}")
|
162
|
-
end
|
163
|
-
end
|
164
|
-
end
|
165
|
-
end
|
166
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "faraday"
|
4
|
+
require "securerandom"
|
5
|
+
require "active_support/all"
|
6
|
+
require "net/http"
|
7
|
+
|
8
|
+
module Net::HTTPHeader
|
9
|
+
def capitalize(name)
|
10
|
+
name
|
11
|
+
end
|
12
|
+
private :capitalize
|
13
|
+
end
|
14
|
+
|
15
|
+
module Aliyun
|
16
|
+
module Connector
|
17
|
+
class ROAClient
|
18
|
+
attr_accessor :endpoint, :api_version, :access_key_id,
|
19
|
+
:access_key_secret, :security_token, :hostname, :opts
|
20
|
+
|
21
|
+
def initialize(config)
|
22
|
+
validate config
|
23
|
+
|
24
|
+
self.endpoint = config[:endpoint]
|
25
|
+
self.api_version = config[:api_version]
|
26
|
+
self.access_key_id = config[:access_key_id]
|
27
|
+
self.access_key_secret = config[:access_key_secret]
|
28
|
+
self.security_token = config[:security_token]
|
29
|
+
end
|
30
|
+
|
31
|
+
def request(method:, uri:, params: {}, body: {}, headers: {}, options: {})
|
32
|
+
# :"Content-Type" => "application/json" to "content-type" => "application/json"
|
33
|
+
headers.deep_transform_keys! { |key| key.to_s.downcase }
|
34
|
+
mix_headers = default_headers.merge(headers)
|
35
|
+
|
36
|
+
response = connection.send(method.downcase) do |request|
|
37
|
+
request.url uri, params
|
38
|
+
if body
|
39
|
+
if mix_headers["content-type"].start_with? "application/json"
|
40
|
+
request_body = body.to_json
|
41
|
+
elsif mix_headers["content-type"].start_with? "application/x-www-form-urlencoded"
|
42
|
+
request_body = URI.encode_www_form(body)
|
43
|
+
else
|
44
|
+
request_body = body
|
45
|
+
end
|
46
|
+
mix_headers["content-md5"] = Digest::MD5.base64digest request_body
|
47
|
+
mix_headers["content-length"] = request_body.length.to_s
|
48
|
+
request.body = request_body
|
49
|
+
end
|
50
|
+
string2sign = string_to_sign(method, uri, mix_headers, params)
|
51
|
+
mix_headers[:authorization] = authorization(string2sign)
|
52
|
+
mix_headers.each { |key, value| request.headers[key] = value }
|
53
|
+
end
|
54
|
+
|
55
|
+
return response if options.has_key? :raw_body
|
56
|
+
|
57
|
+
response_content_type = response.headers["Content-Type"] || ""
|
58
|
+
if response_content_type.start_with?("application/json")
|
59
|
+
if response.status >= 400
|
60
|
+
result = JSON.parse(response.body)
|
61
|
+
raise StandardError, "code: #{response.status}, #{result['Message']} requestid: #{result['RequestId']}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
if response_content_type.start_with?("text/xml")
|
66
|
+
result = Hash.from_xml(response.body)
|
67
|
+
raise ACSError, result["Error"] if result["Error"]
|
68
|
+
end
|
69
|
+
|
70
|
+
response
|
71
|
+
end
|
72
|
+
|
73
|
+
def connection(adapter = Faraday.default_adapter)
|
74
|
+
Faraday.new(url: self.endpoint) { |f| f.adapter adapter }
|
75
|
+
end
|
76
|
+
|
77
|
+
def get(uri: "", headers: {}, params: {}, options: {})
|
78
|
+
request(method: :get, uri: uri, params: params, body: {}, headers: headers, options: options)
|
79
|
+
end
|
80
|
+
|
81
|
+
def post(uri: "", headers: {}, params: {}, body: {}, options: {})
|
82
|
+
request(method: :post, uri: uri, params: params, body: body, headers: headers, options: options)
|
83
|
+
end
|
84
|
+
|
85
|
+
def put(uri: "", headers: {}, params: {}, body: {}, options: {})
|
86
|
+
request(method: :put, uri: uri, params: params, body: body, headers: headers, options: options)
|
87
|
+
end
|
88
|
+
|
89
|
+
def delete(uri: "", headers: {}, params: {}, options: {})
|
90
|
+
request(method: :delete, uri: uri, params: params, body: {}, headers: headers, options: options)
|
91
|
+
end
|
92
|
+
|
93
|
+
def default_headers
|
94
|
+
default_headers = {
|
95
|
+
"accept" => "application/json",
|
96
|
+
"date" => Time.now.httpdate,
|
97
|
+
"host" => URI(self.endpoint).host,
|
98
|
+
"x-acs-signature-nonce" => SecureRandom.hex(16),
|
99
|
+
"x-acs-signature-method" => "HMAC-SHA1",
|
100
|
+
"x-acs-signature-version" => "1.0",
|
101
|
+
"x-acs-version" => self.api_version,
|
102
|
+
"x-sdk-client" => "RUBY(#{RUBY_VERSION})", # FIXME: 如何获取Gem的名称和版本号
|
103
|
+
"user-agent" => DEFAULT_UA
|
104
|
+
}
|
105
|
+
if self.security_token
|
106
|
+
default_headers["x-acs-accesskey-id"] = self.access_key_id
|
107
|
+
default_headers["x-acs-security-token"] = self.security_token
|
108
|
+
end
|
109
|
+
default_headers
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
def string_to_sign(method, uri, headers, query = {})
|
114
|
+
header_string = [
|
115
|
+
method,
|
116
|
+
headers["accept"],
|
117
|
+
headers["content-md5"] || "",
|
118
|
+
headers["content-type"] || "",
|
119
|
+
headers["date"],
|
120
|
+
].join("\n")
|
121
|
+
"#{header_string}\n#{canonicalized_headers(headers)}#{canonicalized_resource(uri, query)}"
|
122
|
+
end
|
123
|
+
|
124
|
+
def canonicalized_headers(headers)
|
125
|
+
headers.keys.select { |key| key.to_s.start_with? "x-acs-" }
|
126
|
+
.sort.map { |key| "#{key}:#{headers[key].strip}\n" }.join
|
127
|
+
end
|
128
|
+
|
129
|
+
def canonicalized_resource(uri, query_hash = {})
|
130
|
+
query_string = query_hash.sort.map { |key, value| "#{key}=#{value}" }.join("&")
|
131
|
+
query_string.empty? ? uri : "#{uri}?#{query_string}"
|
132
|
+
end
|
133
|
+
|
134
|
+
def authorization(string_to_sign)
|
135
|
+
"acs #{self.access_key_id}:#{signature(string_to_sign)}"
|
136
|
+
end
|
137
|
+
|
138
|
+
def signature(string_to_sign)
|
139
|
+
Base64.encode64(OpenSSL::HMAC.digest("sha1", self.access_key_secret, string_to_sign)).strip
|
140
|
+
end
|
141
|
+
|
142
|
+
def validate(config)
|
143
|
+
raise ArgumentError, 'must pass "config"' unless config
|
144
|
+
raise ArgumentError, 'must pass "config[:endpoint]"' unless config[:endpoint]
|
145
|
+
unless config[:endpoint].start_with?("http://") || config[:endpoint].start_with?("https://")
|
146
|
+
raise ArgumentError, '"config.endpoint" must starts with \'https://\' or \'http://\'.'
|
147
|
+
end
|
148
|
+
raise ArgumentError, 'must pass "config[:api_version]"' unless config[:api_version]
|
149
|
+
raise ArgumentError, 'must pass "config[:access_key_id]"' unless config[:access_key_id]
|
150
|
+
raise ArgumentError, 'must pass "config[:access_key_secret]"' unless config[:access_key_secret]
|
151
|
+
end
|
152
|
+
|
153
|
+
class ACSError < StandardError
|
154
|
+
attr_accessor :code
|
155
|
+
|
156
|
+
def initialize(error)
|
157
|
+
self.code = error["Code"]
|
158
|
+
message = error["Message"]
|
159
|
+
host_id = error["HostId"]
|
160
|
+
request_id = error["RequestId"]
|
161
|
+
super("#{message} host_id: #{host_id}, request_id: #{request_id}")
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
@@ -1,116 +1,148 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require "
|
4
|
-
require "
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
self.
|
20
|
-
self.
|
21
|
-
self.
|
22
|
-
self.
|
23
|
-
self.
|
24
|
-
self.
|
25
|
-
|
26
|
-
|
27
|
-
self.codes
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
param_hash
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
1
|
+
require "set"
|
2
|
+
require "openssl"
|
3
|
+
require "faraday"
|
4
|
+
require "active_support/all"
|
5
|
+
|
6
|
+
module Aliyun
|
7
|
+
module Connector
|
8
|
+
class RPCClient
|
9
|
+
|
10
|
+
attr_accessor :endpoint, :api_version, :access_key_id, :access_key_secret,
|
11
|
+
:security_token, :codes, :opts, :verbose
|
12
|
+
|
13
|
+
# 对象初始化属性
|
14
|
+
def initialize(config, verbose = false)
|
15
|
+
|
16
|
+
validate config
|
17
|
+
|
18
|
+
self.endpoint = config[:endpoint]
|
19
|
+
self.api_version = config[:api_version]
|
20
|
+
self.access_key_id = config[:access_key_id] || Aliyun.access_key_id
|
21
|
+
self.access_key_secret = config[:access_key_secret] || Aliyun.access_key_secret
|
22
|
+
self.security_token = config[:security_token]
|
23
|
+
self.opts = config[:opts] || {}
|
24
|
+
self.verbose = verbose.instance_of?(TrueClass) && verbose
|
25
|
+
|
26
|
+
self.codes = Set.new [200, "200", "OK", "Success"]
|
27
|
+
self.codes.merge config[:codes] if config[:codes]
|
28
|
+
end
|
29
|
+
|
30
|
+
# 通用请求接口
|
31
|
+
def request(action:, params: {}, opts: {})
|
32
|
+
opts = self.opts.merge(opts)
|
33
|
+
action = action.upcase_first if opts[:format_action]
|
34
|
+
params = format_params(params) unless opts[:format_params]
|
35
|
+
defaults = default_params
|
36
|
+
params = { Action: action }.merge(defaults).merge(params)
|
37
|
+
method = (opts[:method] || "GET").upcase
|
38
|
+
normalized = normalize(params)
|
39
|
+
canonicalized = canonicalize(normalized)
|
40
|
+
string_to_sign = "#{method}&#{encode('/')}&#{encode(canonicalized)}"
|
41
|
+
secret = "#{self.access_key_secret}&"
|
42
|
+
signature = Base64.encode64(OpenSSL::HMAC.digest("sha1", secret, string_to_sign)).strip
|
43
|
+
normalized.push(["Signature", encode(signature)])
|
44
|
+
|
45
|
+
# 转换为 query 样式
|
46
|
+
querystring = canonicalize(normalized)
|
47
|
+
|
48
|
+
# 特殊处理 POST
|
49
|
+
uri = opts[:method] == "POST" ? "/" : "/?#{querystring}"
|
50
|
+
|
51
|
+
# 初始化会话
|
52
|
+
response = connection.send(method.downcase, uri) do |request|
|
53
|
+
if opts[:method] == "POST"
|
54
|
+
request.headers["Content-Type"] = "application/x-www-form-urlencoded"
|
55
|
+
request.body = querystring
|
56
|
+
end
|
57
|
+
request.headers["User-Agent"] = DEFAULT_UA
|
58
|
+
end
|
59
|
+
|
60
|
+
# 解析接口响应
|
61
|
+
response_body = JSON.parse(response.body)
|
62
|
+
if response_body["Code"] && !response_body["Code"].to_s.empty? && !self.codes.include?(response_body["Code"])
|
63
|
+
raise StandardError, "Code: #{response_body['Code']}, Message: #{response_body['Message']}, URL: #{uri}"
|
64
|
+
end
|
65
|
+
|
66
|
+
response_body
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
def connection(adapter = Faraday.default_adapter)
|
71
|
+
Faraday.new(url: self.endpoint) { |f| f.adapter adapter }
|
72
|
+
end
|
73
|
+
|
74
|
+
# 设置缺省参数
|
75
|
+
def default_params
|
76
|
+
default_params = {
|
77
|
+
"Format" => "JSON",
|
78
|
+
"SignatureMethod" => "HMAC-SHA1",
|
79
|
+
"SignatureNonce" => SecureRandom.hex(16),
|
80
|
+
"SignatureVersion" => "1.0",
|
81
|
+
"Timestamp" => Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ"),
|
82
|
+
"AccessKeyId" => self.access_key_id,
|
83
|
+
"Version" => self.api_version,
|
84
|
+
}
|
85
|
+
default_params["SecurityToken"] = self.security_token if self.security_token
|
86
|
+
default_params
|
87
|
+
end
|
88
|
+
|
89
|
+
# 消息签名需要
|
90
|
+
def encode(string)
|
91
|
+
ERB::Util.url_encode(string)
|
92
|
+
end
|
93
|
+
|
94
|
+
# 转换 HASH key 样式
|
95
|
+
def format_params(param_hash)
|
96
|
+
param_hash.keys.each { |key| param_hash[(key.to_s.upcase_first).to_sym] = param_hash.delete key }
|
97
|
+
param_hash
|
98
|
+
end
|
99
|
+
|
100
|
+
def replace_repeat_list(target, key, repeat)
|
101
|
+
repeat.each_with_index do |item, index|
|
102
|
+
if item && item.instance_of?(Hash)
|
103
|
+
item.each_key { |k| target["#{key}.#{index.next}.#{k}"] = item[k] }
|
104
|
+
else
|
105
|
+
target["#{key}.#{index.next}"] = item
|
106
|
+
end
|
107
|
+
end
|
108
|
+
target
|
109
|
+
end
|
110
|
+
|
111
|
+
def flat_params(params)
|
112
|
+
target = {}
|
113
|
+
params.each do |key, value|
|
114
|
+
if value.instance_of?(Array)
|
115
|
+
replace_repeat_list(target, key, value)
|
116
|
+
else
|
117
|
+
target[key.to_s] = value
|
118
|
+
end
|
119
|
+
end
|
120
|
+
target
|
121
|
+
end
|
122
|
+
|
123
|
+
def normalize(params)
|
124
|
+
flat_params(params).sort.to_h.map { |key, value| [encode(key), encode(value)] }
|
125
|
+
end
|
126
|
+
|
127
|
+
def canonicalize(normalized)
|
128
|
+
normalized.map { |element| "#{element.first}=#{element.last}" }.join("&")
|
129
|
+
end
|
130
|
+
|
131
|
+
def validate(config = {})
|
132
|
+
config.with_indifferent_access
|
133
|
+
raise ArgumentError, 'must pass "config"' unless config
|
134
|
+
raise ArgumentError, 'must pass "config[:endpoint]"' unless config[:endpoint]
|
135
|
+
unless config[:endpoint].match?(/^http[s]?:/i)
|
136
|
+
raise ArgumentError, '"config.endpoint" must starts with \'https://\' or \'http://\'.'
|
137
|
+
end
|
138
|
+
raise ArgumentError, 'must pass "config[:api_version]"' unless config[:api_version]
|
139
|
+
unless config[:access_key_id] || Aliyun.access_key_id
|
140
|
+
raise ArgumentError, 'must pass "config[:access_key_id]" or define "Aliyun.access_key_id"'
|
141
|
+
end
|
142
|
+
unless config[:access_key_secret] || Aliyun.access_key_secret
|
143
|
+
raise ArgumentError, 'must pass "config[:access_key_secret]" or define "Aliyun.access_key_secret"'
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
data/lib/aliyun/dysms.rb
CHANGED
@@ -1,25 +1,25 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Aliyun
|
4
|
-
class Dysms < Aliyun::Connector::RPCClient
|
5
|
-
# 本产品(Dysmsapi/2017-05-25)的OpenAPI采用RPC签名风格,签名细节参见签名机制说明。
|
6
|
-
# 我们已经为开发者封装了常见编程语言的SDK,开发者可通过下载SDK直接调用本产品OpenAPI而无需关心技术细节。
|
7
|
-
def initialize(config = {}, verbose = nil)
|
8
|
-
config[:endpoint] ||= "http://dysmsapi.aliyuncs.com"
|
9
|
-
config[:api_version] ||= "2017-05-25"
|
10
|
-
super(config, verbose)
|
11
|
-
end
|
12
|
-
|
13
|
-
# 发送短信,发送前要申请短信签名和短信模板,并确保签名和模板已审核通过。
|
14
|
-
def send_sms(phone_numbers, template_code, template_param, sign_name = "")
|
15
|
-
params = {
|
16
|
-
PhoneNumbers: phone_numbers,
|
17
|
-
SignName: sign_name,
|
18
|
-
TemplateCode: template_code,
|
19
|
-
TemplateParam: template_param.to_json
|
20
|
-
}
|
21
|
-
opts = { method: "POST", timeout: 15000 }
|
22
|
-
request(action: "SendSms", params: params, opts: opts)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Aliyun
|
4
|
+
class Dysms < Aliyun::Connector::RPCClient
|
5
|
+
# 本产品(Dysmsapi/2017-05-25)的OpenAPI采用RPC签名风格,签名细节参见签名机制说明。
|
6
|
+
# 我们已经为开发者封装了常见编程语言的SDK,开发者可通过下载SDK直接调用本产品OpenAPI而无需关心技术细节。
|
7
|
+
def initialize(config = {}, verbose = nil)
|
8
|
+
config[:endpoint] ||= "http://dysmsapi.aliyuncs.com"
|
9
|
+
config[:api_version] ||= "2017-05-25"
|
10
|
+
super(config, verbose)
|
11
|
+
end
|
12
|
+
|
13
|
+
# 发送短信,发送前要申请短信签名和短信模板,并确保签名和模板已审核通过。
|
14
|
+
def send_sms(phone_numbers, template_code, template_param, sign_name = "")
|
15
|
+
params = {
|
16
|
+
PhoneNumbers: phone_numbers,
|
17
|
+
SignName: sign_name,
|
18
|
+
TemplateCode: template_code,
|
19
|
+
TemplateParam: template_param.to_json
|
20
|
+
}
|
21
|
+
opts = { method: "POST", timeout: 15000 }
|
22
|
+
request(action: "SendSms", params: params, opts: opts)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|