aliyun-rails 0.1.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8fc1dbec7b764f807056f0f18e0f1f39bf8e2cc22292bd22d9c961d1a5946bea
4
+ data.tar.gz: 33574a813a8df2ef5bfd073fb0de6741edf2850690bce64f5941b5ed0b228adf
5
+ SHA512:
6
+ metadata.gz: 19c2df4717e7c95c83348b31ab98b8fa6225e8d0c67e44367071c778615a681284fd6aba5393e8b92630d4ca8236c957521046fa7a6011885a2104727ed55554
7
+ data.tar.gz: c387845817a3214c0576fda0b0d367d03c0fdad3b3d09eda8507a6e658ee36333f1c5623cc5b575be62f1ee305f03caec35b0613581f0f6ea6cb2e91e19630b7
data/README.md ADDED
@@ -0,0 +1,85 @@
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
+
22
+ ## 使用诊断
23
+ [Troubleshoot](https://troubleshoot.api.aliyun.com/?source=github_sdk) 提供 OpenAPI 使用诊断服务,通过 `RequestID` 或 `报错信息` ,帮助开发者快速定位,为开发者提供解决方案。
24
+
25
+ ## 安装
26
+
27
+ ```sh
28
+ $ gem install aliyun-rails
29
+ ```
30
+
31
+ ## 使用
32
+
33
+ RPC 示例;
34
+
35
+ ```ruby
36
+ require "aliyun-rails"
37
+
38
+ client = Dysms.new(
39
+ access_key_id: ENV['ACCESS_KEY_ID'],
40
+ access_key_secret: ENV['ACCESS_KEY_SECRET'],
41
+ )
42
+
43
+ response = client.send_sms("1380000000", "SMS_10010", {param1: "11"}, "SIGN_NAME")
44
+
45
+ puts response
46
+ ```
47
+
48
+
49
+ ROA 示例:
50
+
51
+ ```ruby
52
+ require 'aliyun-rails'
53
+
54
+ client = ROAClient.new(
55
+ endpoint: 'http://ros.aliyuncs.com',
56
+ api_version: '2015-09-01',
57
+ access_key_id: ENV['ACCESS_KEY_ID'],
58
+ access_key_secret: ENV['ACCESS_KEY_SECRET'],
59
+ )
60
+
61
+ response = client.request(
62
+ method: 'GET',
63
+ uri: '/regions',
64
+ options: {
65
+ timeout: 15000
66
+ }
67
+ )
68
+
69
+ print response.body
70
+ ```
71
+
72
+ ## 问题
73
+ [提交 Issue](https://github.com/ciscolive/aliyun-rails/issues/new/choose),不符合指南的问题可能会立即关闭。
74
+
75
+
76
+ ## 发行说明
77
+ 每个版本的详细更改记录在[发行说明](CHANGELOG.md)中。
78
+
79
+
80
+ ## 贡献
81
+ 提交 Pull Request 之前请阅读[贡献指南](CONTRIBUTING.md)。
82
+
83
+
84
+ ## 许可证
85
+ [MIT](LICENSE.md)
data/Rakefile ADDED
@@ -0,0 +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
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "rails/version"
4
+
5
+ require "aliyun/rails/connector/roa_client"
6
+ require "aliyun/rails/connector/rpc_client"
7
+
8
+ module Aliyun
9
+ module Rails
10
+ class Error < StandardError; end
11
+ # Your code goes here...
12
+ end
13
+ end
@@ -0,0 +1,168 @@
1
+ require "faraday"
2
+ require "securerandom"
3
+ require "active_support/all"
4
+ require "net/http"
5
+
6
+ module Net::HTTPHeader
7
+ def capitalize(name)
8
+ name
9
+ end
10
+ private :capitalize
11
+ end
12
+
13
+ module Aliyun
14
+ module Rails
15
+ module Connector
16
+ class ROAClient
17
+
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
+
165
+ end
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,115 @@
1
+ require "set"
2
+ require "openssl"
3
+ require "faraday"
4
+ require "erb"
5
+ require "active_support/all"
6
+
7
+ module Aliyun
8
+ module Rails
9
+ module Connector
10
+ class RPCClient
11
+ attr_accessor :endpoint, :api_version, :access_key_id, :access_key_secret,
12
+ :security_token, :codes, :opts, :verbose
13
+
14
+ def configure
15
+ yield self
16
+ end
17
+
18
+ # 对象初始化属性
19
+ def initialize(config = configure, verbose = false)
20
+
21
+ validate config
22
+
23
+ self.endpoint = config[:endpoint]
24
+ self.api_version = config[:api_version]
25
+ self.access_key_id = config[:access_key_id]
26
+ self.access_key_secret = config[:access_key_secret]
27
+ self.security_token = config[:security_token]
28
+ self.opts = config[:opts] || {}
29
+ self.verbose = verbose.instance_of?(TrueClass) && verbose
30
+ self.codes = Set.new [200, "200", "OK", "Success"]
31
+ self.codes.merge config[:codes] if config[:codes]
32
+ end
33
+
34
+ # 通用请求接口
35
+ def request(action:, params: {}, opts: {})
36
+ opts = self.opts.merge(opts)
37
+ action = upcase_first(action) if opts[:format_action]
38
+ params = format_params(params) unless opts[:format_params]
39
+ defaults = default_params
40
+ params = { Action: action }.merge(defaults).merge(params)
41
+ method = (opts[:method] || "GET").upcase
42
+ sign = "#{method}&#{encode('/')}&#{encode(params.to_query)}"
43
+ secret = "#{self.access_key_secret}&"
44
+ signature = Base64.encode64(OpenSSL::HMAC.digest("sha1", secret, sign)).strip
45
+ params["Signature"] = signature
46
+
47
+ # 转换为 query 样式
48
+ query_string = params.to_query
49
+
50
+ # 特殊处理 POST
51
+ uri = opts[:method] == "POST" ? "/" : "/?#{query_string}"
52
+
53
+ # 初始化会话
54
+ response = connection.send(method.downcase, uri) do |r|
55
+ if opts[:method] == "POST"
56
+ r.headers["Content-Type"] = "application/x-www-form-urlencoded"
57
+ r.body = query_string
58
+ end
59
+ r.headers["User-Agent"] = DEFAULT_UA
60
+ end
61
+
62
+ # 解析接口响应
63
+ response_body = JSON.parse(response.body)
64
+ if response_body["Code"] && !response_body["Code"].to_s.empty? && !self.codes.include?(response_body["Code"])
65
+ raise StandardError, "Code: #{response_body['Code']}, Message: #{response_body['Message']}, URL: #{uri}"
66
+ end
67
+
68
+ response_body
69
+ end
70
+
71
+ private
72
+ def connection(adapter = Faraday.default_adapter)
73
+ Faraday.new(url: self.endpoint) { |f| f.adapter adapter }
74
+ end
75
+
76
+ # 设置缺省参数
77
+ def default_params
78
+ params = {
79
+ Format: "JSON",
80
+ SignatureMethod: "HMAC-SHA1",
81
+ SignatureNonce: SecureRandom.hex(16),
82
+ SignatureVersion: "1.0",
83
+ Timestamp: Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ"),
84
+ AccessKeyId: self.access_key_id,
85
+ Version: self.api_version,
86
+ }
87
+ params[:SecurityToken] = self.security_token if self.security_token.present?
88
+ params
89
+ end
90
+
91
+ # 消息签名需要
92
+ def encode(input)
93
+ ERB::Util.url_encode input
94
+ end
95
+
96
+ # 转换 HASH key 样式
97
+ def format_params(param_hash)
98
+ param_hash.keys.each { |key| param_hash[upcase_first(key.to_s).to_sym] = param_hash.delete key }
99
+ param_hash
100
+ end
101
+
102
+ def validate(config)
103
+ raise ArgumentError, 'must pass "config"' unless config
104
+ raise ArgumentError, 'must pass "config[:endpoint]"' unless config[:endpoint]
105
+ unless config[:endpoint].match?(/^http[s]?:/i)
106
+ raise ArgumentError, '"config.endpoint" must starts with \'https://\' or \'http://\'.'
107
+ end
108
+ raise ArgumentError, 'must pass "config[:api_version]"' unless config[:api_version]
109
+ raise ArgumentError, 'must pass "config[:access_key_id]"' unless config[:access_key_id]
110
+ raise ArgumentError, 'must pass "config[:access_key_secret]"' unless config[:access_key_secret]
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,26 @@
1
+ module Aliyun::Rails
2
+ class Dysms < Aliyun::Rails::Connector::RPCClient
3
+
4
+ # 本产品(Dysmsapi/2017-05-25)的OpenAPI采用RPC签名风格,签名细节参见签名机制说明。
5
+ # 我们已经为开发者封装了常见编程语言的SDK,开发者可通过下载SDK直接调用本产品OpenAPI而无需关心技术细节。
6
+ def initialize(config, verbose = nil)
7
+
8
+ config["endpoint"] = "http://dysmsapi.aliyuncs.com"
9
+ config["api_version"] = "2017-05-25"
10
+
11
+ super
12
+ end
13
+
14
+ # 发送短信,发送前要申请短信签名和短信模板,并确保签名和模板已审核通过。
15
+ def send_sms(phone_numbers, template_code, template_param, sign_name = "")
16
+ params = {
17
+ PhoneNumbers: phone_numbers,
18
+ SignName: sign_name,
19
+ TemplateCode: template_code,
20
+ TemplateParam: template_param.to_json
21
+ }
22
+ opts = { method: "POST", timeout: 15000 }
23
+ request(action: "SendSms", params: params, opts: opts)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ module Aliyun::Rails
2
+ class Dysms < Aliyun::Rails::Connector::RPCClient
3
+
4
+ # 本产品(Dyvmsapi/2017-05-25)的OpenAPI采用RPC签名风格,签名细节参见签名机制说明。
5
+ # 我们已经为开发者封装了常见编程语言的SDK,开发者可通过下载SDK直接调用本产品OpenAPI而无需关心技术细节。
6
+ def initialize(config, verbose = nil)
7
+
8
+ config["endpoint"] = "http://dyvmsapi.aliyuncs.com"
9
+ config["api_version"] = "2017-05-25"
10
+
11
+ super
12
+ end
13
+
14
+ # 调用SingleCallByTts接口向指定号码发送语音验证码和带参数变量的语音通知
15
+ def single_call_by_tts(called_show_number, called_number, tts_code, tts_param)
16
+ params = {
17
+ CalledShowNumber: called_show_number,
18
+ CalledNumber: called_number,
19
+ TtsCode: tts_code,
20
+ TtsParam: tts_param.to_json
21
+ }
22
+ opts = { method: "POST", timeout: 15000 }
23
+ request(action: "SendSms", params: params, opts: opts)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aliyun
4
+ module Rails
5
+ VERSION = "0.1.0"
6
+ DEFAULT_UA = "AlibabaCloud (#{Gem::Platform.local.os}; " +
7
+ "#{Gem::Platform.local.cpu}) Ruby/#{RUBY_VERSION} Core/#{VERSION}"
8
+ end
9
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: aliyun-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - WENWU.YAN
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-03-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 3.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 3.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.15.4
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 0.15.4
41
+ description: 支持 Rails 轻松访问阿里云服务,例如:弹性云主机(ECS)、负载均衡(SLB)、云监控(CloudMonitor)等
42
+ email:
43
+ - careline@foxmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - README.md
49
+ - Rakefile
50
+ - lib/aliyun/aliyun-rails.rb
51
+ - lib/aliyun/rails/connector/roa_client.rb
52
+ - lib/aliyun/rails/connector/rpc_client.rb
53
+ - lib/aliyun/rails/dysms.rb
54
+ - lib/aliyun/rails/dyvms.rb
55
+ - lib/aliyun/rails/version.rb
56
+ homepage: https://github.com/ciscolive/aliyun-rails
57
+ licenses:
58
+ - MIT
59
+ metadata:
60
+ homepage_uri: https://github.com/ciscolive/aliyun-rails
61
+ source_code_uri: https://github.com/ciscolive/aliyun-rails
62
+ changelog_uri: https://github.com/ciscolive/aliyun-rails/blob/main/README.md
63
+ post_install_message:
64
+ rdoc_options: []
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: 2.6.0
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ requirements: []
78
+ rubygems_version: 3.3.3
79
+ signing_key:
80
+ specification_version: 4
81
+ summary: 非阿里云官方SDK,本GEM主要实现RAILS项目轻松调用 ** aliyun ** 相关接口
82
+ test_files: []