aliyun-rails 0.1.15 → 0.1.18

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 485e0e5b14e3aefe6c6e0865cf20ff3a043fe38d012c05f067b1d9d59a21940f
4
- data.tar.gz: d18b4c36f7a38b64a25928a4222c23d7021851267f25f15099c1d8a73c6f9146
3
+ metadata.gz: 2e27be7e1a623946c583fc2d8dfd8144c76352b1370f37954f07d1a92379903d
4
+ data.tar.gz: 73ce2236eacb879c9a4648a1b03701a2f33dfd5ab3060a854fbbb51c54fb50ad
5
5
  SHA512:
6
- metadata.gz: 97971caf232a968deca9341f24613a98f2e671b0d762f2f198fdb266b119bf01994c1858d536817a084e27fb2e143bab3cbc5c1b1da99767f831e6ea6158d64c
7
- data.tar.gz: 18bf61bee58194b9dcb6f462d08ebbf05d117ca0577854723f3ee0a9e68bfff428c99789a04623ca34742bfe918dbf748536f8777e280d6e1829790083f5fa7e
6
+ metadata.gz: 6db85298bf709c4688a92b7c45c34376c043156ae00443882030e1382b6ae36a5eef5456b1c3f317074799440d7edce17751afb1b560a98ad7cef09425014bfb
7
+ data.tar.gz: 5041a64210b45a9cf036e4573bb33a9cbc8a652c2f9fcf167f634477cf816a5daf4709e9e3836718a7bc306e2f424edc9b99f21d686e53579c929e03ddf8b49f
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2022 WENWU.YAN
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md CHANGED
@@ -1,95 +1,153 @@
1
- [English](./README.md) | 简体中文
2
-
3
-
4
- <p align="center">
5
- <a href=" https://www.alibabacloud.com"><img src="https://aliyunsdk-pages.alicdn.com/icons/Aliyun.svg"></a>
6
- </p>
7
-
8
- <h1 align="center">非官方SDK套件-用于RAILS项目管理阿里云资源</h1>
9
-
10
- <p align="center">
11
- <a href="https://badge.fury.io/rb/aliyunsdkcore"><img src="https://badge.fury.io/rb/aliyunsdkcore.svg" alt="Gem Version"></a>
12
- <a href="https://travis-ci.org/aliyun/openapi-core-ruby-sdk"><img src="https://travis-ci.org/aliyun/openapi-core-ruby-sdk.svg?branch=master" alt="Build Status"></a>
13
- <a href="https://ci.appveyor.com/project/aliyun/openapi-core-ruby-sdk/branch/master"><img src="https://ci.appveyor.com/api/projects/status/uyepkk5bjbynofvu/branch/master?svg=true" alt="Build status"></a>
14
- <a href="https://codecov.io/gh/aliyun/openapi-core-ruby-sdk"><img src="https://codecov.io/gh/aliyun/openapi-core-ruby-sdk/branch/master/graph/badge.svg" alt="codecov"></a>
15
- </p>
16
-
17
-
18
- 支持 Rails 轻松访问阿里云服务,例如:弹性云主机(ECS)、负载均衡(SLB)、云监控(CloudMonitor)等。当前已集成短信服务、语音服务功能,后续可以根据业务需要拓展。
19
-
20
- 本文档介绍如何安装和使用 aliyun-rails
21
- 本项目是个人用于集成阿里云短信推送、电话告警功能编排,部分代码借鉴自官方 openapi-core-ruby-sdk
22
-
23
- ## 使用诊断
24
- [Troubleshoot](https://troubleshoot.api.aliyun.com/?source=github_sdk) 提供 OpenAPI 使用诊断服务,通过 `RequestID` 或 `报错信息` ,帮助开发者快速定位,为开发者提供解决方案。
25
-
26
- ## 安装
27
-
28
- ```sh
29
- $ gem install aliyun-rails
30
- ```
31
-
32
- ## 使用
33
-
34
- RPC 示例;
35
-
36
- ```ruby
37
- require "aliyun-rails"
38
-
39
- client = Dysms.new(
40
- access_key_id: ENV['ACCESS_KEY_ID'],
41
- access_key_secret: ENV['ACCESS_KEY_SECRET'],
42
- )
43
-
44
- # 可以直接将API参数放到 initializers下
45
- # Aliyun::Rails.config do |i|
46
- # i.access_key_id = "XXX"
47
- # i.access_key_secret = "YYYY"
48
- # end
49
- # 随后直接初始化
50
- # client = Dysms.new
51
-
52
- # then use the send_sms method
53
- response = client.send_sms("1380000000", "SMS_10010", {param1: "11"}, "SIGN_NAME")
54
-
55
- puts response
56
- ```
57
-
58
-
59
- ROA 示例:
60
-
61
- ```ruby
62
- require 'aliyun-rails'
63
-
64
- client = ROAClient.new(
65
- endpoint: 'http://ros.aliyuncs.com',
66
- api_version: '2015-09-01',
67
- access_key_id: ENV['ACCESS_KEY_ID'],
68
- access_key_secret: ENV['ACCESS_KEY_SECRET'],
69
- )
70
-
71
- response = client.request(
72
- method: 'GET',
73
- uri: '/regions',
74
- options: {
75
- timeout: 15000
76
- }
77
- )
78
-
79
- print response.body
80
- ```
81
-
82
- ## 问题
83
- [提交 Issue](https://github.com/ciscolive/aliyun-rails/issues/new/choose),不符合指南的问题可能会立即关闭。
84
-
85
-
86
- ## 发行说明
87
- 每个版本的详细更改记录在[发行说明](CHANGELOG.md)中。
88
-
89
-
90
- ## 贡献
91
- 提交 Pull Request 之前请阅读[贡献指南](CONTRIBUTING.md)。
92
-
93
-
94
- ## 许可证
95
- [MIT](LICENSE.md)
1
+ [English](./README.md) | 简体中文
2
+
3
+
4
+ <p align="center">
5
+ <a href=" https://www.alibabacloud.com"><img src="https://aliyunsdk-pages.alicdn.com/icons/Aliyun.svg"></a>
6
+ </p>
7
+
8
+ <h1 align="center">非官方SDK套件-用于RAILS项目管理阿里云相关资源,已集成接口鉴权、短信服务、和语音服务功能</h1>
9
+
10
+ <p align="center">
11
+ <a href="https://badge.fury.io/rb/aliyun-rails"><img src="https://badge.fury.io/rb/aliyun-rails.svg" alt="Gem Version"></a>
12
+ <a href="https://travis-ci.org/ciscolive/aliyun-rails"><img src="https://travis-ci.org/ciscolive/aliyun-rails.svg?branch=master" alt="Build Status"></a>
13
+ <a href="https://ci.appveyor.com/project/ciscolive/aliyun-rails/branch/master"><img src="https://ci.appveyor.com/api/projects/status/uyepkk5bjbynofvu/branch/master?svg=true" alt="Build status"></a>
14
+ <a href="https://codecov.io/gh/ciscolive/aliyun-rails"><img src="https://codecov.io/gh/ciscolive/aliyun-rails/branch/master/graph/badge.svg" alt="codecov"></a>
15
+ </p>
16
+
17
+
18
+ - 支持 Rails 轻松访问阿里云服务,例如:弹性云主机(ECS)、负载均衡(SLB)、云监控(CloudMonitor)等。
19
+ - 已集成阿里云短信服务和语音服务,满足日常短信和电话告警需求。
20
+
21
+ 本文档介绍如何安装和使用 aliyun-rails
22
+
23
+ ## 使用诊断
24
+
25
+ [Troubleshoot](https://troubleshoot.api.aliyun.com/?source=github_sdk) 提供 OpenAPI 使用诊断服务,通过 `RequestID` 或 `报错信息`
26
+ ,帮助开发者快速定位,为开发者提供解决方案。
27
+
28
+ ## 安装
29
+
30
+ ```sh
31
+ $ gem install aliyun-rails
32
+ ```
33
+
34
+ ## 使用
35
+
36
+ - 加载模块自动挂载几个常量:Dysms、Dyvms、RPCClient和ROAClient
37
+
38
+ CALL_TTS 实例:
39
+
40
+ ```ruby
41
+ require "aliyun-rails"
42
+
43
+ client = Aliyun::Dyvms.new(
44
+ access_key_id: ENV['ACCESS_KEY_ID'],
45
+ access_key_secret: ENV['ACCESS_KEY_SECRET'],
46
+ )
47
+
48
+ # 可以直接将API参数放到RAILS项目 * config/initializers *下
49
+ # Aliyun.config do |aliyun|
50
+ # aliyun.access_key_id = "XXX"
51
+ # aliyun.access_key_secret = "YYY"
52
+ # end
53
+ # 随后直接初始化
54
+ # client = Aliyun::Dyvms.new
55
+
56
+ # 调用语音方法 single_call_by_tts method
57
+ response = client.single_call_by_tts("075566668888", "13900001234", "TTS_CODE", { TTS_PARAM: 2022 })
58
+
59
+ puts response
60
+
61
+ ```
62
+
63
+ SEND_SMS 示例:
64
+
65
+ ```ruby
66
+ require "aliyun-rails"
67
+
68
+ client = Aliyun::Dysms.new(
69
+ access_key_id: ENV['ACCESS_KEY_ID'],
70
+ access_key_secret: ENV['ACCESS_KEY_SECRET'],
71
+ )
72
+
73
+ # 可以直接将API参数放到RAILS项目 * config/initializers *下
74
+ # Aliyun.config do |aliyun|
75
+ # aliyun.access_key_id = "XXX"
76
+ # aliyun.access_key_secret = "YYY"
77
+ # end
78
+ # 随后直接初始化
79
+ # client = Dysms.new
80
+
81
+ # 调用短信方法 send_sms method
82
+ response = client.send_sms("1380000000", "SMS_10010", { param1: "11" }, "SIGN_NAME")
83
+
84
+ puts response
85
+ ```
86
+
87
+ RPC 示例:
88
+
89
+ ```ruby
90
+ require 'aliyun-rails'
91
+
92
+ # 实例化对象
93
+ client = RPCClient.new(
94
+ endpoint: 'http://ros.aliyuncs.com',
95
+ api_version: '2015-09-01',
96
+ access_key_id: ENV['ACCESS_KEY_ID'],
97
+ access_key_secret: ENV['ACCESS_KEY_SECRET'],
98
+ security_token: 'TOKEN_KEY'
99
+ )
100
+
101
+ # 请求接口查询
102
+ params = { key: (1..11).to_a.map(&:to_s) }
103
+ request_option = { method: 'POST', timeout: 15000 }
104
+ response = client.request(
105
+ action: 'DescribeRegions',
106
+ params: params,
107
+ opts: request_option
108
+ )
109
+
110
+ puts response
111
+
112
+ ```
113
+
114
+ ROA 示例:
115
+
116
+ ```ruby
117
+ require 'aliyun-rails'
118
+
119
+ # 初始化对象
120
+ client = ROAClient.new(
121
+ endpoint: 'http://ros.aliyuncs.com',
122
+ api_version: '2015-09-01',
123
+ access_key_id: ENV['ACCESS_KEY_ID'],
124
+ access_key_secret: ENV['ACCESS_KEY_SECRET'],
125
+ )
126
+
127
+ # 请求接口查询
128
+ response = client.request(
129
+ method: 'GET',
130
+ uri: '/regions',
131
+ options: {
132
+ timeout: 15000
133
+ }
134
+ )
135
+
136
+ puts response.body
137
+ ```
138
+
139
+ ## 问题
140
+
141
+ [提交 Issue](https://github.com/ciscolive/aliyun-rails/issues/new/choose),不符合指南的问题可能会立即关闭。
142
+
143
+ ## 发行说明
144
+
145
+ 每个版本的详细更改记录在[发行说明](CHANGELOG.md)中。
146
+
147
+ ## 贡献
148
+
149
+ 提交 Pull Request 之前请阅读[贡献指南](CONTRIBUTING.md)。
150
+
151
+ ## 许可证
152
+
153
+ [MIT](LICENSE.txt)
data/Rakefile CHANGED
@@ -1,8 +1,8 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/gem_tasks"
4
- require "rubocop/rake_task"
5
-
6
- RuboCop::RakeTask.new
7
-
8
- task default: :rubocop
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rubocop/rake_task"
5
+
6
+ RuboCop::RakeTask.new
7
+
8
+ task default: :rubocop
@@ -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] || Aliyun.access_key_id
27
- self.access_key_secret = config[:access_key_secret] || Aliyun.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,116 @@
1
- # frozen_string_literal: true
2
-
3
- require "set"
4
- require "openssl"
5
- require "faraday"
6
- require "erb"
7
- require "active_support/all"
8
-
9
- module Aliyun
10
- module Connector
11
- class RPCClient
12
- attr_accessor :endpoint, :api_version, :access_key_id, :access_key_secret,
13
- :security_token, :codes, :opts, :verbose
14
-
15
- # 对象初始化属性
16
- def initialize(config = {}, verbose = false)
17
- validate config
18
-
19
- self.endpoint = config[:endpoint]
20
- self.api_version = config[:api_version]
21
- self.access_key_id = config[:access_key_id] || Aliyun.access_key_id
22
- self.access_key_secret = config[:access_key_secret] || Aliyun.access_key_secret
23
- self.security_token = config[:security_token]
24
- self.opts = config[:opts] || {}
25
- self.verbose = verbose.instance_of?(TrueClass) && verbose
26
-
27
- self.codes = Set.new [200, "200", "OK", "Success"]
28
- self.codes.merge config[:codes] if config[:codes]
29
- end
30
-
31
- # 通用请求接口
32
- def request(action:, params: {}, opts: {})
33
- opts = self.opts.merge(opts)
34
- action = action.upcase_first if opts[:format_action]
35
- params = format_params(params) unless opts[:format_params]
36
- defaults = default_params
37
- params = { Action: action }.merge(defaults).merge(params)
38
- method = (opts[:method] || "GET").upcase
39
- sign = "#{method}&#{encode('/')}&#{encode(params.to_query)}"
40
- secret = "#{self.access_key_secret}&"
41
- signature = Base64.encode64(OpenSSL::HMAC.digest("sha1", secret, sign)).strip
42
- params["Signature"] = signature
43
-
44
- # 转换为 query 样式
45
- query_string = params.to_query
46
-
47
- # 特殊处理 POST
48
- uri = opts[:method] == "POST" ? "/" : "/?#{query_string}"
49
-
50
- # 初始化会话
51
- response = connection.send(method.downcase, uri) do |r|
52
- if opts[:method] == "POST"
53
- r.headers["Content-Type"] = "application/x-www-form-urlencoded"
54
- r.body = query_string
55
- end
56
- r.headers["User-Agent"] = DEFAULT_UA
57
- end
58
-
59
- # 解析接口响应
60
- response_body = JSON.parse(response.body)
61
- if response_body["Code"] && !response_body["Code"].to_s.empty? && !self.codes.include?(response_body["Code"])
62
- raise StandardError, "Code: #{response_body['Code']}, Message: #{response_body['Message']}, URL: #{uri}"
63
- end
64
-
65
- response_body
66
- end
67
-
68
- private
69
- def connection(adapter = Faraday.default_adapter)
70
- Faraday.new(url: self.endpoint) { |f| f.adapter adapter }
71
- end
72
-
73
- # 设置缺省参数
74
- def default_params
75
- params = {
76
- Format: "JSON",
77
- SignatureMethod: "HMAC-SHA1",
78
- SignatureNonce: SecureRandom.hex(8),
79
- SignatureVersion: "1.0",
80
- Timestamp: Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ"),
81
- AccessKeyId: self.access_key_id,
82
- Version: self.api_version,
83
- }
84
- params[:SecurityToken] = self.security_token if self.security_token.present?
85
- params
86
- end
87
-
88
- # 消息签名需要
89
- def encode(input)
90
- ERB::Util.url_encode input
91
- end
92
-
93
- # 转换 HASH key 样式
94
- def format_params(param_hash)
95
- param_hash.keys.each { |key| param_hash[(key.to_s.upcase_first).to_sym] = param_hash.delete key }
96
- param_hash
97
- end
98
-
99
- def validate(config = {})
100
- config.with_indifferent_access
101
- raise ArgumentError, 'must pass "config"' unless config
102
- raise ArgumentError, 'must pass "config[:endpoint]"' unless config[:endpoint]
103
- unless config[:endpoint].match?(/^http[s]?:/i)
104
- raise ArgumentError, '"config.endpoint" must starts with \'https://\' or \'http://\'.'
105
- end
106
- raise ArgumentError, 'must pass "config[:api_version]"' unless config[:api_version]
107
- unless config[:access_key_id] || Aliyun.access_key_id
108
- raise ArgumentError, 'must pass "config[:access_key_id]" or define "Aliyun.access_key_id"'
109
- end
110
- unless config[:access_key_secret] || Aliyun.access_key_secret
111
- raise ArgumentError, 'must pass "config[:access_key_secret]" or define "Aliyun.access_key_secret"'
112
- end
113
- end
114
- end
115
- end
116
- end
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+ require "openssl"
5
+ require "faraday"
6
+ require "erb"
7
+ require "active_support/all"
8
+
9
+ module Aliyun
10
+ module Connector
11
+ class RPCClient
12
+ attr_accessor :endpoint, :api_version, :access_key_id, :access_key_secret,
13
+ :security_token, :codes, :opts, :verbose
14
+
15
+ # 对象初始化属性
16
+ def initialize(config = {}, verbose = false)
17
+ validate config
18
+
19
+ self.endpoint = config[:endpoint]
20
+ self.api_version = config[:api_version]
21
+ self.access_key_id = config[:access_key_id] || Aliyun.access_key_id
22
+ self.access_key_secret = config[:access_key_secret] || Aliyun.access_key_secret
23
+ self.security_token = config[:security_token]
24
+ self.opts = config[:opts] || {}
25
+ self.verbose = verbose.instance_of?(TrueClass) && verbose
26
+
27
+ self.codes = Set.new [200, "200", "OK", "Success"]
28
+ self.codes.merge config[:codes] if config[:codes]
29
+ end
30
+
31
+ # 通用请求接口
32
+ def request(action:, params: {}, opts: {})
33
+ opts = self.opts.merge(opts)
34
+ action = action.upcase_first if opts[:format_action]
35
+ params = format_params(params) unless opts[:format_params]
36
+ defaults = default_params
37
+ params = { Action: action }.merge(defaults).merge(params)
38
+ method = (opts[:method] || "GET").upcase
39
+ sign = "#{method}&#{encode('/')}&#{encode(params.to_query)}"
40
+ secret = "#{self.access_key_secret}&"
41
+ signature = Base64.encode64(OpenSSL::HMAC.digest("sha1", secret, sign)).strip
42
+ params["Signature"] = signature
43
+
44
+ # 转换为 query 样式
45
+ query_string = params.to_query
46
+
47
+ # 特殊处理 POST
48
+ uri = opts[:method] == "POST" ? "/" : "/?#{query_string}"
49
+
50
+ # 初始化会话
51
+ response = connection.send(method.downcase, uri) do |r|
52
+ if opts[:method] == "POST"
53
+ r.headers["Content-Type"] = "application/x-www-form-urlencoded"
54
+ r.body = query_string
55
+ end
56
+ r.headers["User-Agent"] = DEFAULT_UA
57
+ end
58
+
59
+ # 解析接口响应
60
+ response_body = JSON.parse(response.body)
61
+ if response_body["Code"] && !response_body["Code"].to_s.empty? && !self.codes.include?(response_body["Code"])
62
+ raise StandardError, "Code: #{response_body['Code']}, Message: #{response_body['Message']}, URL: #{uri}"
63
+ end
64
+
65
+ response_body
66
+ end
67
+
68
+ private
69
+ def connection(adapter = Faraday.default_adapter)
70
+ Faraday.new(url: self.endpoint) { |f| f.adapter adapter }
71
+ end
72
+
73
+ # 设置缺省参数
74
+ def default_params
75
+ params = {
76
+ Format: "JSON",
77
+ SignatureMethod: "HMAC-SHA1",
78
+ SignatureNonce: SecureRandom.hex(8),
79
+ SignatureVersion: "1.0",
80
+ Timestamp: Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ"),
81
+ AccessKeyId: self.access_key_id,
82
+ Version: self.api_version,
83
+ }
84
+ params[:SecurityToken] = self.security_token if self.security_token.present?
85
+ params
86
+ end
87
+
88
+ # 消息签名需要
89
+ def encode(input)
90
+ ERB::Util.url_encode input
91
+ end
92
+
93
+ # 转换 HASH key 样式
94
+ def format_params(param_hash)
95
+ param_hash.keys.each { |key| param_hash[(key.to_s.upcase_first).to_sym] = param_hash.delete key }
96
+ param_hash
97
+ end
98
+
99
+ def validate(config = {})
100
+ config.with_indifferent_access
101
+ raise ArgumentError, 'must pass "config"' unless config
102
+ raise ArgumentError, 'must pass "config[:endpoint]"' unless config[:endpoint]
103
+ unless config[:endpoint].match?(/^http[s]?:/i)
104
+ raise ArgumentError, '"config.endpoint" must starts with \'https://\' or \'http://\'.'
105
+ end
106
+ raise ArgumentError, 'must pass "config[:api_version]"' unless config[:api_version]
107
+ unless config[:access_key_id] || Aliyun.access_key_id
108
+ raise ArgumentError, 'must pass "config[:access_key_id]" or define "Aliyun.access_key_id"'
109
+ end
110
+ unless config[:access_key_secret] || Aliyun.access_key_secret
111
+ raise ArgumentError, 'must pass "config[:access_key_secret]" or define "Aliyun.access_key_secret"'
112
+ end
113
+ end
114
+ end
115
+ end
116
+ 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
data/lib/aliyun/dyvms.rb CHANGED
@@ -1,25 +1,25 @@
1
- # frozen_string_literal: true
2
-
3
- module Aliyun
4
- class Dyvms < Aliyun::Connector::RPCClient
5
- # 本产品(Dyvmsapi/2017-05-25)的OpenAPI采用RPC签名风格,签名细节参见签名机制说明。
6
- # 我们已经为开发者封装了常见编程语言的SDK,开发者可通过下载SDK直接调用本产品OpenAPI而无需关心技术细节。
7
- def initialize(config = {}, verbose = nil)
8
- config[:endpoint] ||= "http://dyvmsapi.aliyuncs.com"
9
- config[:api_version] ||= "2017-05-25"
10
- super(config, verbose)
11
- end
12
-
13
- # 调用SingleCallByTts接口向指定号码发送语音验证码和带参数变量的语音通知
14
- def single_call_by_tts(called_show_number, called_number, tts_code, tts_param)
15
- params = {
16
- CalledShowNumber: called_show_number,
17
- CalledNumber: called_number,
18
- TtsCode: tts_code,
19
- TtsParam: tts_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 Dyvms < Aliyun::Connector::RPCClient
5
+ # 本产品(Dyvmsapi/2017-05-25)的OpenAPI采用RPC签名风格,签名细节参见签名机制说明。
6
+ # 我们已经为开发者封装了常见编程语言的SDK,开发者可通过下载SDK直接调用本产品OpenAPI而无需关心技术细节。
7
+ def initialize(config = {}, verbose = nil)
8
+ config[:endpoint] ||= "http://dyvmsapi.aliyuncs.com"
9
+ config[:api_version] ||= "2017-05-25"
10
+ super(config, verbose)
11
+ end
12
+
13
+ # 调用SingleCallByTts接口向指定号码发送语音验证码和带参数变量的语音通知
14
+ def single_call_by_tts(called_show_number, called_number, tts_code, tts_param)
15
+ params = {
16
+ CalledShowNumber: called_show_number,
17
+ CalledNumber: called_number,
18
+ TtsCode: tts_code,
19
+ TtsParam: tts_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,9 +1,7 @@
1
- # frozen_string_literal: true
2
-
3
- module Aliyun
4
- module Rails
5
- VERSION = "0.1.15"
6
- DEFAULT_UA = "AlibabaCloud (#{Gem::Platform.local.os}; " +
7
- "#{Gem::Platform.local.cpu}) Ruby/#{RUBY_VERSION} Core/#{VERSION}"
8
- end
9
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Aliyun
4
+ VERSION = "0.1.18"
5
+ DEFAULT_UA = "AlibabaCloud (#{Gem::Platform.local.os}; " +
6
+ "#{Gem::Platform.local.cpu}) Ruby/#{RUBY_VERSION} Core/#{VERSION}"
7
+ end
data/lib/aliyun-rails.rb CHANGED
@@ -1,20 +1,25 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "aliyun/version"
4
- require_relative "aliyun/connector/roa_client"
5
- require_relative "aliyun/connector/rpc_client"
6
- require_relative "aliyun/dysms"
7
- require_relative "aliyun/dyvms"
8
-
9
- module Aliyun
10
- class Error < StandardError; end
11
-
12
- class << self
13
- attr_accessor :access_key_id, :access_key_secret
14
- def config
15
- yield self
16
- end
17
- end
18
-
19
- # Your code goes here...
20
- end
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "aliyun/version"
4
+ require_relative "aliyun/connector/roa_client"
5
+ require_relative "aliyun/connector/rpc_client"
6
+ require_relative "aliyun/dysms"
7
+ require_relative "aliyun/dyvms"
8
+
9
+ module Aliyun
10
+ class Error < StandardError; end
11
+
12
+ # 模块单例方法
13
+ class << self
14
+ attr_accessor :access_key_id, :access_key_secret
15
+ def config
16
+ yield self
17
+ end
18
+ end
19
+ end
20
+
21
+ # 加载模块,自动初始化的常量
22
+ RPCClient = Aliyun::Connector::RPCClient
23
+ ROAClient = Aliyun::Connector::ROAClient
24
+ Dysms = Aliyun::Dysms
25
+ Dyvms = Aliyun::Dyvms
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aliyun-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.15
4
+ version: 0.1.18
5
5
  platform: ruby
6
6
  authors:
7
7
  - WENWU.YAN
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-04-02 00:00:00.000000000 Z
11
+ date: 2022-04-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -45,6 +45,7 @@ executables: []
45
45
  extensions: []
46
46
  extra_rdoc_files: []
47
47
  files:
48
+ - LICENSE.txt
48
49
  - README.md
49
50
  - Rakefile
50
51
  - lib/aliyun-rails.rb
@@ -59,8 +60,8 @@ licenses:
59
60
  metadata:
60
61
  homepage_uri: https://github.com/ciscolive/aliyun-rails
61
62
  source_code_uri: https://github.com/ciscolive/aliyun-rails
62
- changelog_uri: https://github.com/ciscolive/aliyun-rails/blob/main/CHANGELOG.md
63
- post_install_message:
63
+ changelog_uri: https://github.com/ciscolive/aliyun-rails/blob/main/README.md
64
+ post_install_message:
64
65
  rdoc_options: []
65
66
  require_paths:
66
67
  - lib
@@ -76,7 +77,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
76
77
  version: '0'
77
78
  requirements: []
78
79
  rubygems_version: 3.3.7
79
- signing_key:
80
+ signing_key:
80
81
  specification_version: 4
81
82
  summary: 非阿里云官方SDK,本GEM主要实现RAILS项目轻松调用 ** aliyun ** 相关接口
82
83
  test_files: []