aliyun-rails 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []