aliyun-opensearch-client 0.1.1

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: fecc8af0c41528c9deedd1a8164541e7dcf5391f99a21101a29c63ac8a75a06d
4
+ data.tar.gz: 16d08793ea28f0596062694fa89f534b2b04d193d26d75b8031bd622e27bc961
5
+ SHA512:
6
+ metadata.gz: d4595ccb2fa168a10055469017b5e1a260f7eb0e3300b5c8e8697d71343823fd81ffdf20fd7d34d1c12693d2c42e9ebe48f5f22bd1e29165bf870bd1f70bc7ed
7
+ data.tar.gz: 2b866f11590a288634ad4eec0b6d337433f68f3c4ea9e5296c540acc2211254ab02c218ed9598eb2f72f0d0bcbdfbc52b75ac44cc2c9280065ad0accdb0a029d
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+
13
+ # ruby version
14
+ .ruby-gemset
15
+ .ruby-version
16
+
17
+ # test sample
18
+ bin/sample
19
+
20
+ # ignore generate gem
21
+ aliyun-openseach-client*.gem
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,52 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2022-06-29 03:29:43 UTC using RuboCop version 1.31.0.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 1
10
+ # Configuration parameters: Include.
11
+ # Include: **/*.gemspec
12
+
13
+ AllCops:
14
+ Exclude:
15
+ - 'spec/**/*'
16
+ NewCops: enable
17
+ SuggestExtensions: false
18
+
19
+ Gemspec/RequiredRubyVersion:
20
+ Include:
21
+ - 2.1.4
22
+
23
+ Layout/LineLength:
24
+ Max: 333
25
+
26
+ Metrics/AbcSize:
27
+ Enabled: true
28
+ Max: 50
29
+
30
+ Metrics/MethodLength:
31
+ Max: 50
32
+
33
+ Style/RedundantSelf:
34
+ Enabled: false
35
+
36
+ Style/RegexpLiteral:
37
+ Enabled: false
38
+
39
+ Style/TrailingCommaInHashLiteral:
40
+ EnforcedStyleForMultiline: consistent_comma
41
+
42
+ Style/RedundantBegin:
43
+ Enabled: false
44
+
45
+ Style/AccessorGrouping:
46
+ Enabled: false
47
+
48
+ Style/SymbolArray:
49
+ Enabled: false
50
+
51
+ Style/FormatStringToken:
52
+ Enabled: false
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ ---
2
+ language: ruby
3
+ cache: bundler
4
+ rvm:
5
+ - 2.7.3
6
+ before_install: gem install bundler -v 2.1.4
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ### 0.1.1
2
+ * Added 完善客户端 - shawn.han
3
+
4
+ ### 0.1.0
5
+ * Init 初始化项目 - shawn.han
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at shawn.han@rccchina.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [https://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: https://contributor-covenant.org
74
+ [version]: https://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in aliyun-opensearch-client.gemspec
6
+ gemspec
7
+
8
+ gem 'rake', '~> 12.0'
9
+
10
+ gem 'ripper-tags', '~> 0.9.1'
11
+
12
+ gem 'pry'
data/Gemfile.lock ADDED
@@ -0,0 +1,102 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ aliyun-opensearch-client (0.1.1)
5
+ faraday (>= 0.10.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ activesupport (6.1.6)
11
+ concurrent-ruby (~> 1.0, >= 1.0.2)
12
+ i18n (>= 1.6, < 2)
13
+ minitest (>= 5.1)
14
+ tzinfo (~> 2.0)
15
+ zeitwerk (~> 2.3)
16
+ addressable (2.8.0)
17
+ public_suffix (>= 2.0.2, < 5.0)
18
+ ast (2.4.2)
19
+ coderay (1.1.3)
20
+ concurrent-ruby (1.1.10)
21
+ crack (0.4.5)
22
+ rexml
23
+ diff-lcs (1.5.0)
24
+ docile (1.4.0)
25
+ faraday (2.3.0)
26
+ faraday-net_http (~> 2.0)
27
+ ruby2_keywords (>= 0.0.4)
28
+ faraday-net_http (2.0.3)
29
+ hashdiff (1.0.1)
30
+ i18n (1.10.0)
31
+ concurrent-ruby (~> 1.0)
32
+ method_source (1.0.0)
33
+ minitest (5.16.2)
34
+ parallel (1.22.1)
35
+ parser (3.1.2.0)
36
+ ast (~> 2.4.1)
37
+ pry (0.14.1)
38
+ coderay (~> 1.1)
39
+ method_source (~> 1.0)
40
+ public_suffix (4.0.7)
41
+ rainbow (3.1.1)
42
+ rake (12.3.3)
43
+ regexp_parser (2.1.1)
44
+ rexml (3.2.5)
45
+ ripper-tags (0.9.1)
46
+ rspec (3.11.0)
47
+ rspec-core (~> 3.11.0)
48
+ rspec-expectations (~> 3.11.0)
49
+ rspec-mocks (~> 3.11.0)
50
+ rspec-core (3.11.0)
51
+ rspec-support (~> 3.11.0)
52
+ rspec-expectations (3.11.0)
53
+ diff-lcs (>= 1.2.0, < 2.0)
54
+ rspec-support (~> 3.11.0)
55
+ rspec-mocks (3.11.1)
56
+ diff-lcs (>= 1.2.0, < 2.0)
57
+ rspec-support (~> 3.11.0)
58
+ rspec-support (3.11.0)
59
+ rubocop (1.31.0)
60
+ parallel (~> 1.10)
61
+ parser (>= 3.1.0.0)
62
+ rainbow (>= 2.2.2, < 4.0)
63
+ regexp_parser (>= 1.8, < 3.0)
64
+ rexml (>= 3.2.5, < 4.0)
65
+ rubocop-ast (>= 1.18.0, < 2.0)
66
+ ruby-progressbar (~> 1.7)
67
+ unicode-display_width (>= 1.4.0, < 3.0)
68
+ rubocop-ast (1.18.0)
69
+ parser (>= 3.1.1.0)
70
+ ruby-progressbar (1.11.0)
71
+ ruby2_keywords (0.0.5)
72
+ simplecov (0.21.2)
73
+ docile (~> 1.1)
74
+ simplecov-html (~> 0.11)
75
+ simplecov_json_formatter (~> 0.1)
76
+ simplecov-html (0.12.3)
77
+ simplecov_json_formatter (0.1.4)
78
+ tzinfo (2.0.4)
79
+ concurrent-ruby (~> 1.0)
80
+ unicode-display_width (2.0.0)
81
+ webmock (3.14.0)
82
+ addressable (>= 2.8.0)
83
+ crack (>= 0.3.2)
84
+ hashdiff (>= 0.4.0, < 2.0.0)
85
+ zeitwerk (2.6.0)
86
+
87
+ PLATFORMS
88
+ ruby
89
+
90
+ DEPENDENCIES
91
+ activesupport
92
+ aliyun-opensearch-client!
93
+ pry
94
+ rake (~> 12.0)
95
+ ripper-tags (~> 0.9.1)
96
+ rspec
97
+ rubocop
98
+ simplecov
99
+ webmock
100
+
101
+ BUNDLED WITH
102
+ 2.1.4
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2022 shawn.han
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 ADDED
@@ -0,0 +1,60 @@
1
+ # Aliyun::Opensearch::Client 🧯
2
+
3
+ 本项目是阿里云Opensearch服务的ruby sdk, 定位类似[rsolr](https://github.com/rsolr/rsolr)之于solr
4
+ 会根据[官方文档](https://help.aliyun.com/document_detail/54237.html)生成流量API的签名, 并发送请求
5
+ 该gem最低支持的ruby版本为2.1.4
6
+
7
+ ## Install
8
+
9
+ 安装gem
10
+
11
+ ```ruby
12
+ gem install 'aliyun-opensearch-client'
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ```ruby
18
+ # 1. 添加配置
19
+ Aliyun::Opensearch::Configuration.endpoint = 'opensearch-cn-hangzhou.aliyuncs.com'
20
+ Aliyun::Opensearch::Configuration.access_key_id = 'A*****************z'
21
+ Aliyun::Opensearch::Configuration.access_key_secret = 'z*********************A'
22
+
23
+ # 2. 创建客户端实例
24
+ client = Aliyun::Opensearch::Client.new('your_app_name')
25
+
26
+ # 3. 发送请求
27
+ # 插入
28
+ puts client.insert('your_table', [ { id: 1, title: 'Cien años de soledad' }, { id: 2, title: 'Les Misérables' } ]) # true
29
+ puts client.insert('your_table', { id: 3, title: 'The Metamorphosis' }) # true
30
+
31
+ # 更新
32
+ puts client.update('your_table', [ { id: 1, title: 'Book - 百年孤独' }, { id: 2, title: 'Book - 悲惨世界' } ]) # true
33
+ puts client.update('your_table', { id: 3, title: 'Book - 变形记' }) # true
34
+
35
+ # 搜索
36
+ puts client.search({query: "query=default:'Book'"})
37
+ # {"searchtime"=>0.102566, "total"=>3, "num"=>3, "viewtotal"=>3, "compute_cost"=>[{"index_name"=>"your_app_name", "value"=>0.355}], "items"=>[{"id"=>"2", "index_name"=>"your_app_name"}, {"id"=>"1", "index_name"=>"your_app_name"}, {"id"=>"3", "index_name"=>"your_app_name"}], "facet"=>[]}
38
+
39
+ # 删除
40
+ puts client.delete('your_table', { id: 1 }) # true
41
+ puts client.delete('your_table', [{ id: 2 }, { id: 3 }]) # true
42
+
43
+ # 自定义接口请求
44
+ response = client.call(:get, '/v3/openapi/apps/your_app_name/suggest/your_suggest_name/search', { query: '...' })
45
+ ```
46
+
47
+ ## Feature
48
+ - 流量API签名认证
49
+ - 封装流量API的 数据处理 / 搜索处理 接口
50
+ - 兼容请求未封装的流量API接口
51
+
52
+ ## RoadMap
53
+ - request日志追踪
54
+ - 支持流量API的 推送采集数据 / 下拉提示 / 热搜和底纹 接口
55
+ - 支持管控API (可以临时用Aliyun官方的OpenAPI解决问题)
56
+ - 支持STS认证
57
+
58
+ ## 开源协议
59
+
60
+ [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/aliyun/opensearch/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'aliyun-opensearch-client'
7
+ spec.version = Aliyun::Opensearch::VERSION
8
+ spec.authors = ['shawn.han']
9
+ spec.email = ['shawn.han@rccchina.com']
10
+
11
+ spec.summary = 'Aliyun Opensearch 服务客户端'
12
+ spec.description = '对接Aliyun Opensearch 的流量API HTTP接口, 生成签名并发送请求'
13
+ spec.homepage = 'https://github.com/rccgroup/aliyun-opensearch-client'
14
+ spec.license = 'MIT'
15
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.1.4')
16
+
17
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org/'
18
+ spec.metadata['rubygems_mfa_required'] = 'true'
19
+
20
+ spec.metadata['homepage_uri'] = spec.homepage
21
+ spec.metadata['source_code_uri'] = 'https://github.com/rccgroup/aliyun-opensearch-client'
22
+ spec.metadata['changelog_uri'] = 'https://github.com/rccgroup/aliyun-opensearch-client/blob/master/CHANGELOG.md'
23
+
24
+ # Specify which files should be added to the gem when it is released.
25
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
26
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
27
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
28
+ end
29
+ spec.bindir = 'exe'
30
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
+ spec.require_paths = ['lib']
32
+
33
+ spec.add_dependency 'faraday', '>= 0.10.0'
34
+
35
+ spec.add_development_dependency 'activesupport'
36
+ spec.add_development_dependency 'rspec'
37
+ spec.add_development_dependency 'rubocop'
38
+ spec.add_development_dependency 'simplecov'
39
+ spec.add_development_dependency 'webmock'
40
+ end
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'aliyun/opensearch'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require 'irb'
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ # require client_ext folder ruby file
4
+ %w[traffic].each do |file|
5
+ require File.join(File.dirname(__FILE__), 'client_ext', file)
6
+ end
7
+
8
+ module Aliyun
9
+ module Opensearch
10
+ #
11
+ # Opensearch客户端
12
+ #
13
+ # @author shawn.han <shawn.han@rccchina.com>
14
+ #
15
+ class Client
16
+ include ClientExt::Traffic::Behavior
17
+
18
+ # 允许请求的action
19
+ REQUEST_METHODS = [:get, :post].freeze
20
+
21
+ # 应用名称
22
+ attr_reader :app
23
+
24
+ #
25
+ # 客户端初始化
26
+ #
27
+ # @author shawn.han <shawn.han@rccchina.com>
28
+ #
29
+ # @param [String] app 应用名称, 即要访问的Opensearch应用的名称, 在实例管理里可以看到
30
+ #
31
+ def initialize(app)
32
+ @app = app
33
+ @connection = Faraday.new(url: Configuration.opensearch_url) do |faraday|
34
+ faraday.response :raise_error
35
+ end
36
+ end
37
+
38
+ #
39
+ # 发送请求
40
+ #
41
+ # @author shawn.han <shawn.han@rccchina.com>
42
+ #
43
+ # @param [String|Symbol] method 接口请求方法
44
+ # @param [String] path 接口地址
45
+ # @param [Hash] params 接口参数
46
+ #
47
+ # @return [Hash] 请求结果
48
+ #
49
+ def call(method, path, params)
50
+ raise ArgumentError, "Not Allow Request Method #{method}; Only Allowed #{REQUEST_METHODS}" unless REQUEST_METHODS.map { |e| [e, e.to_s, e.to_s.upcase] }.flatten.include?(method)
51
+
52
+ response = @connection.send(method.downcase, path, params) do |request|
53
+ api_params = (request.http_method == :get ? request.params : request.body)
54
+ request.headers = ClientExt::Traffic::Header.new(request.http_method, request.path, api_params: api_params).generate
55
+ end
56
+ body = response.body.empty? ? {} : JSON.parse(response.body)
57
+
58
+ valid_response(response, body)
59
+ body
60
+ rescue Error::AliyunService, Error::InvalidResponseBody => e
61
+ raise e
62
+ rescue Faraday::Error => e
63
+ raise Error::HttpBase, e
64
+ end
65
+
66
+ #
67
+ # 返回结果校验
68
+ #
69
+ # @author shawn.han <shawn.han@rccchina.com>
70
+ #
71
+ def valid_response(response, body)
72
+ raise Error::InvalidResponseBody, response if body.empty? || !body.keys.include?('status')
73
+ raise Error::AliyunService, response if body['status'] != 'OK'
74
+ end
75
+ end
76
+ end
77
+ end
File without changes
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aliyun
4
+ module Opensearch
5
+ module ClientExt
6
+ module Traffic
7
+ #
8
+ # 各种接口行为
9
+ #
10
+ # @author shawn.han <shwan.han@rccchina.com>
11
+ #
12
+ module Behavior
13
+ API_SEARCH = '/v3/openapi/apps/%{app_name}/search'
14
+ API_ACTION = '/v3/openapi/apps/%{app_name}/%{table_name}/actions/bulk'
15
+
16
+ ACTION_CMD_KEY = 'cmd'
17
+ ACTION_CMD_INSERT = 'add'
18
+ ACTION_CMD_UPDATE = 'update'
19
+ ACTION_CMD_DELETE = 'delete'
20
+
21
+ #
22
+ # 搜索接口
23
+ #
24
+ # @author shawn.han <shawn.han@rccchina.com>
25
+ #
26
+ # @param [Hash] params 参数
27
+ # @see https://help.aliyun.com/document_detail/57155.html
28
+ #
29
+ # @return [Hash] 查询结果
30
+ #
31
+ def search(params)
32
+ raise Error::EmptyParam if params.nil? || params.empty?
33
+
34
+ self.call(:get, complete_path(API_SEARCH), params)['result']
35
+ end
36
+
37
+ #
38
+ # 插入操作
39
+ #
40
+ # @author shawn.han <shawn.han@rccchina.com>
41
+ #
42
+ # @param [String] table_name 操作对象的表名
43
+ # @param [Hash] params 参数
44
+ # @see https://help.aliyun.com/document_detail/57154.html
45
+ #
46
+ # @return [Boolean] 处理结果
47
+ #
48
+ def insert(table_name, params)
49
+ action(table_name, ACTION_CMD_INSERT, params)
50
+ end
51
+
52
+ #
53
+ # 更新操作
54
+ #
55
+ # @author shawn.han <shawn.han@rccchina.com>
56
+ #
57
+ # @param [String] table_name 操作对象的表名
58
+ # @param [Hash] params 参数
59
+ # @see https://help.aliyun.com/document_detail/57154.html
60
+ #
61
+ # @return [Boolean] 处理结果
62
+ #
63
+ def update(table_name, params)
64
+ action(table_name, ACTION_CMD_UPDATE, params)
65
+ end
66
+
67
+ #
68
+ # 删除操作
69
+ #
70
+ # @author shawn.han <shawn.han@rccchina.com>
71
+ #
72
+ # @param [String] table_name 操作对象的表名
73
+ # @param [Hash] params 参数
74
+ # @see https://help.aliyun.com/document_detail/57154.html
75
+ #
76
+ # @return [Boolean] 处理结果
77
+ #
78
+ def delete(table_name, params)
79
+ action(table_name, ACTION_CMD_DELETE, params)
80
+ end
81
+
82
+ #
83
+ # 操作接口
84
+ # @note 操作接口的几个注意点(此为Aliyun Opensearch服务内部的处理)
85
+ # 1. add操作时, 如果数据已经存在, 会直接更新这条数据, 也不会报错
86
+ # 2. update操作时, 如果数据不存在, 不会直接新建数据, 也不会报错
87
+ # 3. delete操作时, 如果数据不存在, 不会进行操作, 也不会报错
88
+ #
89
+ # @author shawn.han <shawn.han@rccchina.com>
90
+ #
91
+ # @param [String] table_name 操作对象的表名
92
+ # @param [String] action 操作类型
93
+ # @param [Array|Hash] params 参数
94
+ #
95
+ # @return [Boolean] 处理结果
96
+ #
97
+ def action(table_name, action, params)
98
+ raise Error::EmptyParam if params.nil? || params.empty?
99
+
100
+ self.call(:post, complete_path(API_ACTION, table_name: table_name), JSON.generate(complete_action_params(params, action)))['result']
101
+ end
102
+
103
+ private
104
+
105
+ #
106
+ # 返回完整的接口路径
107
+ #
108
+ # @author shawn.han <shawn.han@rccchina.com>
109
+ #
110
+ # @param [String] path 接口路径
111
+ # @param [String] table_name 表名
112
+ #
113
+ # @return [String] 完整的接口路径
114
+ #
115
+ def complete_path(path, table_name: nil)
116
+ options = { app_name: self.app }
117
+ options.merge!(table_name: table_name) unless table_name.nil?
118
+
119
+ format(path, options)
120
+ end
121
+
122
+ #
123
+ # 返回完整的参数
124
+ #
125
+ # @author shawn.han <shawn.han@rccchina.com>
126
+ #
127
+ # @param [Array|Hash] params 参数
128
+ # @param [String] action 操作类型
129
+ #
130
+ # @return [Array<Hash>] 完整的参数
131
+ #
132
+ def complete_action_params(params, action)
133
+ # 判断是否需要补充fields字段
134
+ need_cmplete_fields_key = proc { |param| (['fields', :fields] & param.keys).empty? || (!param['fields'].is_a?(Hash) && !param[:fields].is_a?(Hash)) }
135
+
136
+ # 添加操作类型
137
+ if params.is_a?(Array)
138
+ params = params.map do |e|
139
+ e = { 'fields' => e } if need_cmplete_fields_key.call(e)
140
+ e.merge(ACTION_CMD_KEY => action)
141
+ end
142
+ else
143
+ params = { 'fields' => params } if need_cmplete_fields_key.call(params)
144
+ params = [params.merge(ACTION_CMD_KEY => action)]
145
+ end
146
+
147
+ params
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aliyun
4
+ module Opensearch
5
+ module ClientExt
6
+ module Traffic
7
+ class Header
8
+ #
9
+ # HTTP请求header - 请求认证模块
10
+ #
11
+ # @author shawn.han <shawn.han@rccchina.com>
12
+ #
13
+ module Credentials
14
+ SIGN_PREFIX = 'OPENSEARCH'
15
+
16
+ def authorization
17
+ "#{SIGN_PREFIX} #{@access_key_id}:#{signature}"
18
+ end
19
+
20
+ def signature
21
+ @signature ||= begin
22
+ sign_body = [@request_method, self.content_md5, self.content_type, self.date, canonicalized_open_search_headers, canonicalized_resource].join("\n")
23
+
24
+ Base64.encode64(
25
+ OpenSSL::HMAC.digest(
26
+ OpenSSL::Digest.new('sha1'),
27
+ @access_key_secret,
28
+ sign_body
29
+ )
30
+ )
31
+ end
32
+ end
33
+
34
+ #
35
+ # 生成 CanonicalizedOpenSearchHeaders
36
+ #
37
+ # @author shawn.han <shawn.han@rccchina.com>
38
+ #
39
+ # @see https://help.aliyun.com/document_detail/54237.html#section-92h-jsb-rlu
40
+ #
41
+ # @return [String] CanonicalizedOpenSearchHeaders
42
+ #
43
+ def canonicalized_open_search_headers
44
+ @canonicalized_open_search_headers ||= self.x_opensearch_headers.to_a.sort { |x, y| x[0] <=> y[0] }.map do |e|
45
+ "#{e[0].downcase}:#{e[1]}"
46
+ end.join("\n")
47
+ end
48
+
49
+ #
50
+ # 生成 CanonicalizedResource
51
+ #
52
+ # @author shawn.han <shawn.han@rccchina.com>
53
+ #
54
+ # @see https://help.aliyun.com/document_detail/54237.htm
55
+ #
56
+ # @return [String] CanonicalizedResource
57
+ #
58
+ def canonicalized_resource
59
+ path = @path
60
+ query = nil
61
+
62
+ if @request_method == 'GET'
63
+ # 按照阿里云文档生成query, see: https://help.aliyun.com/document_detail/54237.htm#section-ni9-hgi-tal
64
+ query = @api_params.to_a.sort { |x, y| x[0] <=> y[0] }.map do |e|
65
+ unless e[1].nil?
66
+ [e[0], (e[1].to_s == '' ? nil : e[1].to_s)]
67
+ end
68
+ end.compact
69
+
70
+ # encode_www_form方法文档中描述一些字符不会转移, 这里手动进行处理
71
+ # This method doesn't convert *, -, ., 0-9, A-Z, _, a-z, but does convert SP (ASCII space) to + and converts others to %XX.
72
+ query = URI.encode_www_form(query).gsub('+', '%20').gsub('*', '%2A')
73
+ end
74
+
75
+ [path, query].compact.reject(&:empty?).join('?')
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ # require header folder ruby file
4
+ %w[credentials].each do |file|
5
+ require File.join(File.dirname(__FILE__), 'header', file)
6
+ end
7
+
8
+ module Aliyun
9
+ module Opensearch
10
+ module ClientExt
11
+ module Traffic
12
+ #
13
+ # 流量API HTTP请求header
14
+ #
15
+ # @author shawn.han <shawn.han@rccchina.com>
16
+ #
17
+ class Header
18
+ # 默认 conten-type
19
+ DEFAULT_CONTENT_TYPE = 'application/json'
20
+ # 默认 user-agent, 内容参考阿里云官方openapi sdk: https://github.com/aliyun/openapi-core-ruby-sdk/blob/master/lib/aliyunsdkcore.rb
21
+ DEFAULT_UA = "AlibabaCloud (#{Gem::Platform.local.os}; #{Gem::Platform.local.cpu}) Ruby/#{RUBY_VERSION} Core/ALiyunOpensearchClient-#{Aliyun::Opensearch::VERSION}"
22
+
23
+ # nonce后缀最小长度
24
+ NONCE_SUFFIX_MIN = 100_000
25
+ # nonce后缀最大长度
26
+ NONCE_SUFFIX_MAX = 999_999
27
+
28
+ include Credentials # 签名认证模块
29
+
30
+ #
31
+ # 创建实例
32
+ #
33
+ # @author shawn.han <shawn.han@rccchina.com>
34
+ #
35
+ # @param [String] request_method 接口请求方法(GET POST...)
36
+ # @param [String] path 请求资源路径
37
+ # @param [Hash] api_params 请求接口参数
38
+ # @param [String] access_key_id 阿里云 AccessKeyId
39
+ # @param [String] access_key_secret 阿里云 AccessKeySecret
40
+ #
41
+ def initialize(request_method, path, api_params: nil, access_key_id: nil, access_key_secret: nil)
42
+ @request_method = request_method.to_s.upcase
43
+ @path = path
44
+
45
+ @api_params = api_params
46
+
47
+ @access_key_id = access_key_id || Configuration.access_key_id
48
+ @access_key_secret = access_key_secret || Configuration.access_key_secret
49
+ end
50
+
51
+ #
52
+ # 生成Header的Hash结构
53
+ #
54
+ # @author shawn.han <shawn.han@rccchina.com>
55
+ #
56
+ # @return [Hash] 请求用到的所有Header内容
57
+ #
58
+ def generate
59
+ result = {
60
+ 'User-Agent' => DEFAULT_UA,
61
+ 'Content-Type' => content_type,
62
+ 'Date' => date,
63
+ }.merge(x_opensearch_headers)
64
+ result.merge!('Content-Md5' => content_md5) if content_md5
65
+ result.merge!('Authorization' => authorization)
66
+ result
67
+ end
68
+
69
+ #
70
+ # 以X-Opensearch开头的header
71
+ #
72
+ # @author shawn.han <shawn.han@rccchina.com>
73
+ #
74
+ # @return [Hash] 以X-Opensearch开头的header
75
+ #
76
+ def x_opensearch_headers
77
+ {
78
+ 'X-Opensearch-Nonce' => x_opensearch_nonce,
79
+ }
80
+ end
81
+
82
+ #
83
+ # hearder中 Content-Type的值
84
+ #
85
+ # @author shawn.han <shawn.han@rccchina.com>
86
+ #
87
+ # @return [String] Content-Type的值
88
+ #
89
+ def content_type
90
+ @content_type ||= DEFAULT_CONTENT_TYPE
91
+ end
92
+
93
+ #
94
+ # 生成 header中 X-Opensearch-Nonce的值
95
+ #
96
+ # @author shawn.han <shawn.han@rccchina.com>
97
+ #
98
+ # @see https://help.aliyun.com/document_detail/54237.html#section-92h-jsb-rlu
99
+ #
100
+ # @return [String] X-Opensearch-Nonce的值
101
+ #
102
+ def x_opensearch_nonce
103
+ @x_opensearch_nonce ||= "#{time_now.to_i}#{rand(NONCE_SUFFIX_MIN..NONCE_SUFFIX_MAX)}"
104
+ end
105
+
106
+ #
107
+ # 生成 header中 Date的值
108
+ #
109
+ # @author shawn.han <shawn.han@rccchina.com>
110
+ #
111
+ # @return [String] Date的值
112
+ #
113
+ def date
114
+ @date ||= time_now.utc.strftime('%Y-%m-%dT%H:%M:%SZ')
115
+ end
116
+
117
+ #
118
+ # 生成 header中 Content-MD5的值
119
+ #
120
+ # @author shawn.han <shawn.han@rccchina.com>
121
+ #
122
+ # @return [String] Content-MD5的值
123
+ #
124
+ def content_md5
125
+ Digest::MD5.hexdigest(@api_params.to_json) if @request_method == 'POST' && @api_params
126
+ end
127
+
128
+ private
129
+
130
+ #
131
+ # 当前时间
132
+ #
133
+ # @author shawn.han <shawn.han@rccchina.com>
134
+ #
135
+ # @return [Time] 当前时间
136
+ #
137
+ def time_now
138
+ @time_now ||= Time.now
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # require traffic folder ruby file
4
+ %w[header behavior].each do |file|
5
+ require File.join(File.dirname(__FILE__), 'traffic', file)
6
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aliyun
4
+ module Opensearch
5
+ #
6
+ # 服务配置
7
+ #
8
+ # @author shawn.han <shawn.han@rccchina.com>
9
+ #
10
+ class Configuration
11
+ class << self
12
+ attr_accessor :endpoint # Opensearch服务地址
13
+ attr_accessor :access_key_id # 阿里云申请的ak
14
+ attr_accessor :access_key_secret # 阿里云申请的ak secret
15
+
16
+ #
17
+ # 根据设置的endpoint获取服务的url, 如果没有协议头默认设置为https
18
+ #
19
+ # @author shawn.han <shawn.han@rccchina.com>
20
+ #
21
+ # @return [String] 服务url
22
+ #
23
+ def opensearch_url
24
+ endpoint =~ /^https?:\/\// ? endpoint : "https://#{endpoint}"
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aliyun
4
+ module Opensearch
5
+ #
6
+ # 异常模块
7
+ #
8
+ # @author shawn.han <shawn.han@rccchina.com>
9
+ #
10
+ module Error
11
+ #
12
+ # 请求异常基类
13
+ #
14
+ # @author shawn.han <shawn.han@rccchina.com>
15
+ #
16
+ class HttpBase < Faraday::Error
17
+ end
18
+
19
+ #
20
+ # 阿里云异常基类
21
+ #
22
+ # @author shawn.han <shawn.han@rccchina.com>
23
+ #
24
+ class AliyunService < HttpBase
25
+ ERRCODE_ALIYUN_SYS = (1000..1999).freeze # 系统级别(1000-1999)
26
+ ERRCODE_ALIYUN_APP = (2000..2999).freeze # 应用相关(2000-2999)
27
+ ERRCODE_ALIYUN_DOC = (3000..3999).freeze # 文档相关(3000-3999)
28
+ ERRCODE_ALIYUN_AUTH = (4000..4999).freeze # 授权相关(4000-4999)
29
+ ERRCODE_ALIYUN_USER = (5000..5999).freeze # 用户相关(5000-5999)
30
+ ERRCODE_ALIYUN_SEARCH = (6000..6999).freeze # 搜索相关(6000-6999)
31
+ ERRCODE_ALIYUN_CMD = (7000..7999).freeze # 数据处理相关(7000-7999)
32
+ ERRCODE_ALIYUN_COMMON = (8000..8999).freeze # 文档错误内部通知(8000-8999)
33
+ ERRCODE_ALIYUN_TEMPLETE = (9000..9999).freeze # 模板相关(9000-9999)
34
+ ERRCODE_ALIYUN_SYNC = (10_000..).freeze # 数据同步相关(10000-)
35
+
36
+ attr_reader :response
37
+
38
+ #
39
+ # 初始化
40
+ #
41
+ # @author shawn.han <shawn.han@rccchina.com>
42
+ #
43
+ # @param [Faraday::Response] response 接口返回结果
44
+ #
45
+ def initialize(response)
46
+ @response = response
47
+ @body = JSON.parse(response.body)
48
+
49
+ super("RequestId:#{@body['request_id']} Message:#{errors_message}", response)
50
+ end
51
+
52
+ #
53
+ # 获取错误信息
54
+ #
55
+ # @author shawn.han <shawn.han@rccchina.com>
56
+ #
57
+ # @return [String] 错误消息
58
+ #
59
+ def errors_message
60
+ @body['errors'].map do |error|
61
+ "[#{error['code']}] #{error['message']}"
62
+ end.join("\n")
63
+ end
64
+ end
65
+
66
+ #
67
+ # 空参数异常
68
+ #
69
+ # @author shawn.han <shawn.han@rccchina.com>
70
+ #
71
+ class EmptyParam < ArgumentError
72
+ #
73
+ # 初始化
74
+ #
75
+ # @author shawn.han <shawn.han@rccchina.com>
76
+ #
77
+ def initialize
78
+ super('Param Can Not Be Empty')
79
+ end
80
+ end
81
+
82
+ #
83
+ # 无效返回结果异常
84
+ #
85
+ # @author shawn.han <shawn.han@rccchina.com>
86
+ #
87
+ class InvalidResponseBody < HttpBase
88
+ #
89
+ # 初始化
90
+ #
91
+ # @author shawn.han <shawn.han@rccchina.com>
92
+ #
93
+ def initialize(response)
94
+ super('Response Body is Invalid', response)
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aliyun
4
+ module Opensearch
5
+ VERSION = '0.1.1'
6
+ end
7
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'base64'
4
+ require 'digest'
5
+ require 'faraday'
6
+ require 'json'
7
+ require 'openssl'
8
+
9
+ require 'aliyun/opensearch/client'
10
+ require 'aliyun/opensearch/configuration'
11
+ require 'aliyun/opensearch/error'
12
+ require 'aliyun/opensearch/version'
13
+
14
+ module Aliyun
15
+ #
16
+ # Aliyun Opensearch SDK
17
+ #
18
+ # @author shawn.han <shawn.han@rccchina.coms>
19
+ #
20
+ module Opensearch
21
+ end
22
+ end
metadata ADDED
@@ -0,0 +1,156 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: aliyun-opensearch-client
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - shawn.han
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2022-07-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.10.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.10.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: webmock
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: 对接Aliyun Opensearch 的流量API HTTP接口, 生成签名并发送请求
98
+ email:
99
+ - shawn.han@rccchina.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".gitignore"
105
+ - ".rspec"
106
+ - ".rubocop.yml"
107
+ - ".travis.yml"
108
+ - CHANGELOG.md
109
+ - CODE_OF_CONDUCT.md
110
+ - Gemfile
111
+ - Gemfile.lock
112
+ - LICENSE.txt
113
+ - README.md
114
+ - Rakefile
115
+ - aliyun-opensearch-client.gemspec
116
+ - bin/console
117
+ - bin/setup
118
+ - lib/aliyun/opensearch.rb
119
+ - lib/aliyun/opensearch/client.rb
120
+ - lib/aliyun/opensearch/client_ext/management/.keep
121
+ - lib/aliyun/opensearch/client_ext/traffic.rb
122
+ - lib/aliyun/opensearch/client_ext/traffic/behavior.rb
123
+ - lib/aliyun/opensearch/client_ext/traffic/header.rb
124
+ - lib/aliyun/opensearch/client_ext/traffic/header/credentials.rb
125
+ - lib/aliyun/opensearch/configuration.rb
126
+ - lib/aliyun/opensearch/error.rb
127
+ - lib/aliyun/opensearch/version.rb
128
+ homepage: https://github.com/rccgroup/aliyun-opensearch-client
129
+ licenses:
130
+ - MIT
131
+ metadata:
132
+ allowed_push_host: https://rubygems.org/
133
+ rubygems_mfa_required: 'true'
134
+ homepage_uri: https://github.com/rccgroup/aliyun-opensearch-client
135
+ source_code_uri: https://github.com/rccgroup/aliyun-opensearch-client
136
+ changelog_uri: https://github.com/rccgroup/aliyun-opensearch-client/blob/master/CHANGELOG.md
137
+ post_install_message:
138
+ rdoc_options: []
139
+ require_paths:
140
+ - lib
141
+ required_ruby_version: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: 2.1.4
146
+ required_rubygems_version: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ requirements: []
152
+ rubygems_version: 3.1.6
153
+ signing_key:
154
+ specification_version: 4
155
+ summary: Aliyun Opensearch 服务客户端
156
+ test_files: []