antbird 0.15.0 → 1.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.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/build-os.yml +6 -27
  3. data/.gitignore +2 -2
  4. data/Gemfile.lock +66 -0
  5. data/README.md +72 -15
  6. data/antbird.gemspec +3 -4
  7. data/lib/antbird/client.rb +113 -61
  8. data/lib/antbird/rest_api/rest_api_opensearch_v3_0.rb +492 -328
  9. data/lib/antbird/rest_api/rest_api_opensearch_v3_1.rb +495 -330
  10. data/lib/antbird/rest_api/rest_api_opensearch_v3_2.rb +495 -330
  11. data/lib/antbird/rest_api/rest_api_opensearch_v3_3.rb +495 -330
  12. data/lib/antbird/rest_api/rest_api_opensearch_v3_4.rb +1505 -0
  13. data/lib/antbird/rest_api/rest_api_opensearch_v3_5.rb +1505 -0
  14. data/lib/antbird/version.rb +1 -1
  15. metadata +8 -71
  16. data/.github/workflows/build.yml +0 -45
  17. data/lib/antbird/rest_api/rest_api_opensearch_v1_0.rb +0 -1221
  18. data/lib/antbird/rest_api/rest_api_opensearch_v1_1.rb +0 -1221
  19. data/lib/antbird/rest_api/rest_api_opensearch_v1_2.rb +0 -1221
  20. data/lib/antbird/rest_api/rest_api_opensearch_v1_3.rb +0 -1221
  21. data/lib/antbird/rest_api/rest_api_opensearch_v2_0.rb +0 -1204
  22. data/lib/antbird/rest_api/rest_api_opensearch_v2_1.rb +0 -1204
  23. data/lib/antbird/rest_api/rest_api_opensearch_v2_10.rb +0 -1332
  24. data/lib/antbird/rest_api/rest_api_opensearch_v2_11.rb +0 -1332
  25. data/lib/antbird/rest_api/rest_api_opensearch_v2_12.rb +0 -1332
  26. data/lib/antbird/rest_api/rest_api_opensearch_v2_13.rb +0 -1332
  27. data/lib/antbird/rest_api/rest_api_opensearch_v2_14.rb +0 -1332
  28. data/lib/antbird/rest_api/rest_api_opensearch_v2_15.rb +0 -1332
  29. data/lib/antbird/rest_api/rest_api_opensearch_v2_16.rb +0 -1332
  30. data/lib/antbird/rest_api/rest_api_opensearch_v2_17.rb +0 -1332
  31. data/lib/antbird/rest_api/rest_api_opensearch_v2_18.rb +0 -1332
  32. data/lib/antbird/rest_api/rest_api_opensearch_v2_19.rb +0 -1332
  33. data/lib/antbird/rest_api/rest_api_opensearch_v2_2.rb +0 -1204
  34. data/lib/antbird/rest_api/rest_api_opensearch_v2_3.rb +0 -1212
  35. data/lib/antbird/rest_api/rest_api_opensearch_v2_4.rb +0 -1292
  36. data/lib/antbird/rest_api/rest_api_opensearch_v2_5.rb +0 -1292
  37. data/lib/antbird/rest_api/rest_api_opensearch_v2_6.rb +0 -1300
  38. data/lib/antbird/rest_api/rest_api_opensearch_v2_7.rb +0 -1324
  39. data/lib/antbird/rest_api/rest_api_opensearch_v2_8.rb +0 -1332
  40. data/lib/antbird/rest_api/rest_api_opensearch_v2_9.rb +0 -1332
  41. data/lib/antbird/rest_api/rest_api_v6_4.rb +0 -996
  42. data/lib/antbird/rest_api/rest_api_v6_5.rb +0 -1012
  43. data/lib/antbird/rest_api/rest_api_v6_6.rb +0 -1012
  44. data/lib/antbird/rest_api/rest_api_v6_7.rb +0 -1012
  45. data/lib/antbird/rest_api/rest_api_v6_8.rb +0 -1011
  46. data/lib/antbird/rest_api/rest_api_v7_0.rb +0 -1012
  47. data/lib/antbird/rest_api/rest_api_v7_1.rb +0 -1012
  48. data/lib/antbird/rest_api/rest_api_v7_10.rb +0 -1189
  49. data/lib/antbird/rest_api/rest_api_v7_11.rb +0 -1189
  50. data/lib/antbird/rest_api/rest_api_v7_12.rb +0 -1197
  51. data/lib/antbird/rest_api/rest_api_v7_13.rb +0 -3045
  52. data/lib/antbird/rest_api/rest_api_v7_14.rb +0 -3141
  53. data/lib/antbird/rest_api/rest_api_v7_15.rb +0 -3189
  54. data/lib/antbird/rest_api/rest_api_v7_2.rb +0 -1020
  55. data/lib/antbird/rest_api/rest_api_v7_3.rb +0 -1012
  56. data/lib/antbird/rest_api/rest_api_v7_4.rb +0 -1028
  57. data/lib/antbird/rest_api/rest_api_v7_5.rb +0 -1028
  58. data/lib/antbird/rest_api/rest_api_v7_6.rb +0 -1044
  59. data/lib/antbird/rest_api/rest_api_v7_7.rb +0 -1092
  60. data/lib/antbird/rest_api/rest_api_v7_8.rb +0 -1157
  61. data/lib/antbird/rest_api/rest_api_v7_9.rb +0 -1181
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0e72bd2b5755abdb64e991345baab5cdca8320fa1f511753e9c8845245b03427
4
- data.tar.gz: 427b4282927adafe574eda5151c6b9abe5023bf03c148348deee995569264adb
3
+ metadata.gz: 2d00e228d8e4628e1e0a0f0fb34e0df4382c552266b1510f44734ef022757569
4
+ data.tar.gz: 6d4d6abd75ee27a4756eac51b6039b44f438f378ca4fe23ccd95905ce61eee25
5
5
  SHA512:
6
- metadata.gz: 23752fde1a9a786036e97cc0fb3fd5d99ed803d5cdfdf2c5cdde557f4ffca28653f929e97f77d818eb5ee2437d10a6f7937a499b4f48be828674da984c84c00a
7
- data.tar.gz: 0e6472c29d2ea5736a24898b0306310ce13b1f26bd93d2c84d09d54a8bfa8ddbb8b3defa0f0ed27389429bef1e434568f6ec11910e75f083f00b27d102f39575
6
+ metadata.gz: 0a0e44ac7de86a2671b3571ba407dd262f01ed976bb70648e397d112ccd28b5c169397a528231105fc9fd7b175f427185beadfd0508cf523b000104313a92059
7
+ data.tar.gz: e2aaf781014694479d8710dfefa417a1e29b993600ecf043aab7b44713e66630143e3a4ae1550bc9328184d5b6a4948b47d4daa2f6b19f36681ae8c44e57e6ae
@@ -13,34 +13,12 @@ jobs:
13
13
  matrix:
14
14
  # https://github.com/opensearch-project/OpenSearch/releases
15
15
  search_versions:
16
+ - 3.5.0
17
+ - 3.4.0
16
18
  - 3.3.2
17
19
  - 3.2.0
18
20
  - 3.1.0
19
21
  - 3.0.0
20
- - 2.19.4
21
- - 2.18.0
22
- - 2.17.1
23
- - 2.16.0
24
- - 2.15.0
25
- - 2.14.0
26
- - 2.13.0
27
- - 2.12.0
28
- - 2.11.1
29
- - 2.10.0
30
- - 2.9.0
31
- - 2.8.0
32
- - 2.7.0
33
- - 2.6.0
34
- - 2.5.0
35
- - 2.4.1
36
- - 2.3.0
37
- - 2.2.1
38
- - 2.1.0
39
- - 2.0.1
40
- - 1.3.20
41
- - 1.2.4
42
- - 1.1.0
43
- - 1.0.0
44
22
  services:
45
23
  opensearch:
46
24
  image: opensearchproject/opensearch:${{matrix.search_versions}}
@@ -49,7 +27,8 @@ jobs:
49
27
  env:
50
28
  discovery.type: single-node
51
29
  plugins.security.disabled: true
52
- OPENSEARCH_INITIAL_ADMIN_PASSWORD: StrongPassowrd123
30
+ # a minimum 8 character password and must contain at least one uppercase letter, one lowercase letter, one digit, and one special character that is strong. Password strength can be tested here: https://lowe.github.io/tryzxcvbn
31
+ OPENSEARCH_INITIAL_ADMIN_PASSWORD: "antbird@123Antbird"
53
32
  steps:
54
33
  - name: Configure sysctl limits
55
34
  run: |
@@ -60,8 +39,8 @@ jobs:
60
39
  - uses: actions/checkout@v6
61
40
  - uses: ruby/setup-ruby@v1
62
41
  with:
63
- ruby-version: "3.2"
42
+ ruby-version: "4.0"
64
43
  bundler-cache: true
65
44
  - name: Wait for OpenSearch
66
- run: timeout 60 bash -c "until curl --silent --output /dev/null 'localhost:9200/_cat/health?h=st'; do printf '.'; sleep 5; done; printf '\n'"
45
+ run: timeout 120 bash -c "until curl --silent --output /dev/null 'localhost:9200/_cat/health?h=st'; do printf '.'; sleep 5; done; printf '\n'"
67
46
  - run: bundle exec rspec
data/.gitignore CHANGED
@@ -10,6 +10,6 @@
10
10
  # rspec failure tracking
11
11
  .rspec_status
12
12
 
13
- Gemfile.lock
14
-
15
13
  .envrc
14
+
15
+ .claude/settings.local.json
data/Gemfile.lock ADDED
@@ -0,0 +1,66 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ antbird (1.1.0)
5
+ faraday (>= 2.0.1)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ addressable (2.9.0)
11
+ public_suffix (>= 2.0.2, < 8.0)
12
+ connection_pool (3.0.2)
13
+ diff-lcs (1.6.2)
14
+ faraday (2.14.2)
15
+ faraday-net_http (>= 2.0, < 3.5)
16
+ json
17
+ logger
18
+ faraday-net_http (3.4.2)
19
+ net-http (~> 0.5)
20
+ faraday-net_http_persistent (2.3.1)
21
+ faraday (~> 2.5)
22
+ net-http-persistent (>= 4.0.4, < 5)
23
+ faraday-retry (2.4.0)
24
+ faraday (~> 2.0)
25
+ json (2.19.5)
26
+ logger (1.7.0)
27
+ net-http (0.9.1)
28
+ uri (>= 0.11.1)
29
+ net-http-persistent (4.0.8)
30
+ connection_pool (>= 2.2.4, < 4)
31
+ octokit (10.0.0)
32
+ faraday (>= 1, < 3)
33
+ sawyer (~> 0.9)
34
+ public_suffix (7.0.5)
35
+ rake (13.3.1)
36
+ rspec (3.13.2)
37
+ rspec-core (~> 3.13.0)
38
+ rspec-expectations (~> 3.13.0)
39
+ rspec-mocks (~> 3.13.0)
40
+ rspec-core (3.13.6)
41
+ rspec-support (~> 3.13.0)
42
+ rspec-expectations (3.13.5)
43
+ diff-lcs (>= 1.2.0, < 2.0)
44
+ rspec-support (~> 3.13.0)
45
+ rspec-mocks (3.13.8)
46
+ diff-lcs (>= 1.2.0, < 2.0)
47
+ rspec-support (~> 3.13.0)
48
+ rspec-support (3.13.7)
49
+ sawyer (0.9.3)
50
+ addressable (>= 2.3.5)
51
+ faraday (>= 0.17.3, < 3)
52
+ uri (1.1.1)
53
+
54
+ PLATFORMS
55
+ ruby
56
+
57
+ DEPENDENCIES
58
+ antbird!
59
+ faraday-net_http_persistent
60
+ faraday-retry
61
+ octokit
62
+ rake (~> 13.0)
63
+ rspec (~> 3.0)
64
+
65
+ BUNDLED WITH
66
+ 4.0.8
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Antbird
2
2
 
3
- Nearly auto-generated Elasticsearch/OpenSearch client
3
+ Nearly auto-generated OpenSearch client (supports OpenSearch 3.x)
4
4
 
5
- [![Build Status](https://github.com/fukayatsu/antbird/workflows/build/badge.svg)](https://github.com/fukayatsu/antbird/actions)
5
+ [![Build Status](https://github.com/fukayatsu/antbird/workflows/build-os/badge.svg)](https://github.com/fukayatsu/antbird/actions)
6
6
 
7
7
  ## Installation
8
8
 
@@ -29,7 +29,7 @@ client = Antbird::Client.new
29
29
 
30
30
  # OR
31
31
 
32
- client = Antbird::Client.new.scoped(index: 'test-index', type: 'test-type')
32
+ client = Antbird::Client.new.scoped(index: 'test-index')
33
33
 
34
34
  # OR
35
35
 
@@ -37,19 +37,17 @@ client = Antbird::Client.new(
37
37
  url: 'http://localhost:9200',
38
38
  scope: {
39
39
  index: 'test-index',
40
- type: 'test-type'
41
40
  }
42
41
  )
43
42
 
44
- # OR
43
+ # OR with AWS OpenSearch Service
45
44
 
46
45
  require 'faraday/net_http_persistent'
47
46
  require 'faraday_middleware/aws_sigv4'
48
47
  client = Antbird::Client.new(
49
- url: ENV['AMAZON_ELASTICSEARCH_SERVICE_URL'],
48
+ url: ENV['AMAZON_OPENSEARCH_SERVICE_URL'],
50
49
  scope: {
51
50
  index: 'test-index',
52
- type: 'test-type'
53
51
  },
54
52
  adapter: :net_http_persistent
55
53
  ) do |f|
@@ -68,7 +66,7 @@ end
68
66
 
69
67
  ```ruby
70
68
  client = Antbird::Client.new(
71
- scope: { index: 'test-index', type: 'test-type' }
69
+ scope: { index: 'test-index' }
72
70
  )
73
71
 
74
72
  client.indices_exists? # => false
@@ -76,10 +74,8 @@ client.indices_create(
76
74
  body: {
77
75
  settings: { number_of_shards: 1 },
78
76
  mappings: {
79
- 'test-type': {
80
- properties: {
81
- field1: { type: :text }
82
- }
77
+ properties: {
78
+ field1: { type: :text }
83
79
  }
84
80
  }
85
81
  }
@@ -103,6 +99,67 @@ client.bulk(body: [
103
99
  ])
104
100
  ```
105
101
 
102
+ ### Timeouts
103
+
104
+ Default connection timeouts are configured when the client is created:
105
+
106
+ ```ruby
107
+ client = Antbird::Client.new(
108
+ read_timeout: 5, # seconds (default)
109
+ open_timeout: 2, # seconds (default)
110
+ write_timeout: 30, # seconds (default: nil => falls back to read_timeout)
111
+ )
112
+ ```
113
+
114
+ `write_timeout` is optional. When omitted it is left unset and the adapter
115
+ falls back to `read_timeout` for the write phase, preserving existing behavior.
116
+
117
+ Timeouts can be overridden per operation. There are two ways to do it:
118
+
119
+ 1. `http_timeout` — a shorthand that sets the `read`, `open` and `write`
120
+ timeouts all at once for that single request:
121
+
122
+ ```ruby
123
+ # This request alone uses a 60s timeout for read/open/write.
124
+ client.search(body: { query: { match_all: {} } }, http_timeout: 60)
125
+
126
+ # Long-running reindex; give it more time without affecting other calls.
127
+ client.reindex(body: { source: { index: 'a' }, dest: { index: 'b' } }, http_timeout: 600)
128
+ ```
129
+
130
+ 2. `read_timeout` / `open_timeout` / `write_timeout` — override individual
131
+ phases. These may be combined with each other:
132
+
133
+ ```ruby
134
+ client.bulk(body: [{ index: { _id: '1' } }, { field1: 'a' }], open_timeout: 3, write_timeout: 30)
135
+ ```
136
+
137
+ When none of these is given, the client falls back to the values configured at
138
+ initialization time.
139
+
140
+ `http_timeout` is mutually exclusive with `read_timeout` / `open_timeout` /
141
+ `write_timeout`. Passing `http_timeout` together with any of them raises an
142
+ `ArgumentError`:
143
+
144
+ ```ruby
145
+ client.search(body: { query: { match_all: {} } }, http_timeout: 60, open_timeout: 3) # => ArgumentError
146
+ ```
147
+
148
+ All of the above are client-side (HTTP) timeouts and do not collide with the
149
+ server-side `timeout` query parameter that some OpenSearch APIs accept — both
150
+ can be passed together:
151
+
152
+ ```ruby
153
+ client.bulk(
154
+ body: [{ index: { _id: '1' } }, { field1: 'a' }],
155
+ timeout: '30s', # OpenSearch server-side timeout (query parameter)
156
+ http_timeout: 60, # HTTP read/open/write timeout (Faraday)
157
+ )
158
+ ```
159
+
160
+ > `read_timeout` sets Faraday's global `:timeout` (preserving its original
161
+ > behavior), while `open_timeout` / `write_timeout` set those specific phases.
162
+
106
163
  ## Development
107
164
 
108
165
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -113,9 +170,9 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
113
170
 
114
171
  Bug reports and pull requests are welcome on GitHub at https://github.com/fukayatsu/antbird. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
115
172
 
116
- ### How to support newer version of Elasticsearch/OpenSearch
173
+ ### How to support newer version of OpenSearch
117
174
 
118
- 1. Add Elasticsearch version to `jobs.build.strategy.matrix.search_versions` on `.github/workflows/build.yml` / `.github/workflows/build-os.yml`
175
+ 1. Add OpenSearch version to `jobs.build.strategy.matrix.search_versions` on `.github/workflows/build-os.yml`
119
176
  - Only one line for `x.y.*` version
120
177
  1. `script/generate_api_methods`
121
178
  - Set `GITHUB_TOKEN=***` env for GitHub API Limit
@@ -126,4 +183,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
126
183
 
127
184
  ## Code of Conduct
128
185
 
129
- Everyone interacting in the Antbird projects codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/fukayatsu/antbird/blob/master/CODE_OF_CONDUCT.md).
186
+ Everyone interacting in the Antbird project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/fukayatsu/antbird/blob/master/CODE_OF_CONDUCT.md).
data/antbird.gemspec CHANGED
@@ -9,8 +9,8 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["fukayatsu"]
10
10
  spec.email = ["fukayatsu@gmail.com"]
11
11
 
12
- spec.summary = %q{Nearly auto-generated Elasticsearch client}
13
- spec.description = %q{Antbird is a yet another low level API client for the Elasticsearch HTTP interface.}
12
+ spec.summary = %q{Nearly auto-generated OpenSearch client}
13
+ spec.description = %q{Antbird is a yet another low level API client for the OpenSearch HTTP interface.}
14
14
  spec.homepage = "https://github.com/fukayatsu/antbird"
15
15
  spec.license = "MIT"
16
16
 
@@ -20,13 +20,12 @@ Gem::Specification.new do |spec|
20
20
  spec.bindir = "exe"
21
21
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
22
  spec.require_paths = ["lib"]
23
- spec.required_ruby_version = ">= 2.7.0"
23
+ spec.required_ruby_version = ">= 3.3.0"
24
24
 
25
25
  spec.add_dependency "faraday", ">= 2.0.1"
26
26
 
27
27
  spec.add_development_dependency "faraday-retry"
28
28
  spec.add_development_dependency "faraday-net_http_persistent"
29
- spec.add_development_dependency "bundler", "> 1.16", "< 3.0"
30
29
  spec.add_development_dependency "rake", "~> 13.0"
31
30
  spec.add_development_dependency "rspec", "~> 3.0"
32
31
  spec.add_development_dependency "octokit"
@@ -7,29 +7,27 @@ module Antbird
7
7
  scope: {},
8
8
  url: "http://localhost:9200",
9
9
  version: nil,
10
- distribution: nil,
11
10
  read_timeout: 5,
12
11
  open_timeout: 2,
12
+ write_timeout: nil,
13
13
  adapter: ::Faraday.default_adapter,
14
14
  &block)
15
15
 
16
- @read_timeout = read_timeout
17
- @open_timeout = open_timeout
18
- @adapter = adapter
19
- @block = block
20
- @url = url
16
+ @read_timeout = read_timeout
17
+ @open_timeout = open_timeout
18
+ @write_timeout = write_timeout
19
+ @adapter = adapter
20
+ @block = block
21
+ @url = url
21
22
 
22
23
  @scope = scope.transform_keys(&:to_sym)
23
24
 
24
- if version
25
- @version = version
26
- @distribution = distribution
27
- end
25
+ @version = version
28
26
 
29
27
  @api_specs = {}
30
28
  end
31
- attr_reader :scope, :url, :distribution
32
- attr_reader :read_timeout, :open_timeout, :adapter
29
+ attr_reader :scope, :url
30
+ attr_reader :read_timeout, :open_timeout, :write_timeout, :adapter
33
31
  attr_reader :api_specs, :last_request
34
32
 
35
33
  def scoped(new_scope = {})
@@ -37,17 +35,19 @@ module Antbird
37
35
  scope: new_scope,
38
36
  url: url,
39
37
  version: version,
40
- distribution: distribution,
41
38
  read_timeout: read_timeout,
42
- open_timeout: open_timeout
39
+ open_timeout: open_timeout,
40
+ write_timeout: write_timeout,
41
+ adapter: adapter,
42
+ &@block
43
43
  )
44
44
  end
45
45
 
46
- def request(api_name, api_spec, params)
47
- validate_params(api_spec, params)
46
+ def request(api_name, api_spec, params, path_params = [:index, :type, :id])
47
+ validate_params(api_spec, params, path_params)
48
48
 
49
49
  body = extract_body(params)
50
- scopes = extract_scopes(params)
50
+ scopes = extract_scopes(params, path_params)
51
51
  forced_method = extract_method(params)
52
52
 
53
53
  # Greedy match
@@ -87,7 +87,7 @@ module Antbird
87
87
  methods.first
88
88
  end
89
89
 
90
- read_timeout = params.delete(:read_timeout)
90
+ timeout_options = extract_timeout_options(params)
91
91
  params.reject! { |_, v| v.nil? }
92
92
 
93
93
  @last_request = {
@@ -103,29 +103,29 @@ module Antbird
103
103
  when :head
104
104
  connection.head(api_path) do |req|
105
105
  req.params = params unless params.empty?
106
- req.options[:timeout] = read_timeout if read_timeout
106
+ apply_timeouts(req, timeout_options)
107
107
  end
108
108
  when :get
109
109
  connection.get(api_path) do |req|
110
110
  req.params = params unless params.empty?
111
111
  req.body = body if body
112
- req.options[:timeout] = read_timeout if read_timeout
112
+ apply_timeouts(req, timeout_options)
113
113
  end
114
114
  when :put
115
115
  connection.put(api_path, body) do |req|
116
116
  req.params = params unless params.empty?
117
- req.options[:timeout] = read_timeout if read_timeout
117
+ apply_timeouts(req, timeout_options)
118
118
  end
119
119
  when :post
120
120
  connection.post(api_path, body) do |req|
121
121
  req.params = params unless params.empty?
122
- req.options[:timeout] = read_timeout if read_timeout
122
+ apply_timeouts(req, timeout_options)
123
123
  end
124
124
  when :delete
125
125
  connection.delete(api_path) do |req|
126
126
  req.params = params unless params.empty?
127
127
  req.body = body if body
128
- req.options[:timeout] = read_timeout if read_timeout
128
+ apply_timeouts(req, timeout_options)
129
129
  end
130
130
  else
131
131
  raise ArgumentError, "Unknown HTTP request method: #{method.inspect}"
@@ -148,9 +148,9 @@ module Antbird
148
148
  params.delete(:method)&.to_sym
149
149
  end
150
150
 
151
- def extract_scopes(params)
151
+ def extract_scopes(params, path_params = [:index, :type, :id])
152
152
  scopes = {}
153
- [:index, :type, :id].each do |s|
153
+ path_params.each do |s|
154
154
  scope = params.delete(s)
155
155
  next unless scope
156
156
  scopes[s] = scope
@@ -188,44 +188,61 @@ module Antbird
188
188
  conn.request :json
189
189
  conn.response :json, content_type: /\bjson$/
190
190
 
191
- conn.options[:timeout] = read_timeout
192
- conn.options[:open_timeout] = open_timeout
191
+ conn.options[:timeout] = read_timeout
192
+ conn.options[:open_timeout] = open_timeout
193
+ conn.options[:write_timeout] = write_timeout if write_timeout
193
194
 
194
195
  conn.adapter adapter
195
196
  end
196
197
  end
197
198
 
198
- def elasticsearch?
199
- !opensearch?
200
- end
201
-
202
- def opensearch?
203
- ensure_version_and_distribution
199
+ def version
200
+ return @version if @version
204
201
 
205
- distribution == 'opensearch'
202
+ ensure_version
203
+ @version
206
204
  end
207
205
 
208
- def elasticsearch_v7_0_compatible?
209
- return true if opensearch?
206
+ private
210
207
 
211
- elasticsearch? && Gem::Version.new(version) >= Gem::Version.new('7.0.0')
212
- end
208
+ # Builds the per-operation Faraday timeout options from the request params.
209
+ # - http_timeout: overrides read/open/write timeouts all at once. It is
210
+ # mutually exclusive with read_timeout/open_timeout/write_timeout.
211
+ # - read_timeout: legacy per-operation override (sets Faraday's :timeout).
212
+ # - open_timeout / write_timeout: per-operation overrides for those phases.
213
+ # The granular options may be combined with each other.
214
+ # When none is given, the connection-level defaults are used.
215
+ #
216
+ # The read phase is set via Faraday's :timeout key (consistent with the
217
+ # legacy read_timeout path and the connection-level default); :open_timeout
218
+ # and :write_timeout are set explicitly so http_timeout overrides them even
219
+ # when the connection configures its own defaults.
220
+ def extract_timeout_options(params)
221
+ http_timeout = params.delete(:http_timeout)
222
+ read_timeout = params.delete(:read_timeout)
223
+ open_timeout = params.delete(:open_timeout)
224
+ write_timeout = params.delete(:write_timeout)
225
+
226
+ if http_timeout && (read_timeout || open_timeout || write_timeout)
227
+ raise ArgumentError,
228
+ ":http_timeout cannot be combined with :read_timeout, :open_timeout or :write_timeout"
229
+ end
213
230
 
214
- def elasticsearch_v7_6_compatible?
215
- return true if opensearch?
231
+ if http_timeout
232
+ return { timeout: http_timeout, open_timeout: http_timeout, write_timeout: http_timeout }
233
+ end
216
234
 
217
- elasticsearch? && Gem::Version.new(version) >= Gem::Version.new('7.6.0')
235
+ options = {}
236
+ options[:timeout] = read_timeout if read_timeout
237
+ options[:open_timeout] = open_timeout if open_timeout
238
+ options[:write_timeout] = write_timeout if write_timeout
239
+ options
218
240
  end
219
241
 
220
- def version
221
- return @version if @version
222
-
223
- ensure_version_and_distribution
224
- @version
242
+ def apply_timeouts(req, timeout_options)
243
+ timeout_options.each { |key, value| req.options[key] = value }
225
244
  end
226
245
 
227
- private
228
-
229
246
  # NOTE: stable sort
230
247
  def sort_url_paths(url_paths)
231
248
  i = 0
@@ -246,16 +263,50 @@ module Antbird
246
263
  end
247
264
  end
248
265
 
249
- def validate_params(api_spec, params)
250
- # TODO case: required parameter is missing
251
- # TODO case: invalid parameter name
252
- # TODO case: invalid parameter format
266
+ SPECIAL_PARAMS = %i[body method http_timeout read_timeout open_timeout write_timeout].freeze
267
+
268
+ def validate_params(api_spec, params, path_params)
253
269
  if api_spec.dig('body', 'required') && !params.key?(:body)
254
270
  raise ArgumentError, 'Body is missing'
255
271
  end
272
+
273
+ return unless api_spec.key?('params')
274
+
275
+ api_params = api_spec['params']
276
+ common = respond_to?(:common_params) ? common_params.fetch('params', {}).keys.map(&:to_sym) : []
277
+ valid_keys = api_params.keys.map(&:to_sym) + path_params + SPECIAL_PARAMS + common
278
+
279
+ unknown = params.keys - valid_keys
280
+ unless unknown.empty?
281
+ raise ArgumentError, "Unknown parameters: #{unknown.join(', ')}"
282
+ end
283
+
284
+ api_params.each do |name, defn|
285
+ if defn['required'] && !params.key?(name.to_sym)
286
+ raise ArgumentError, "Required parameter missing: #{name}"
287
+ end
288
+
289
+ value = params[name.to_sym]
290
+ next if value.nil?
291
+
292
+ validate_param_type(name, value, defn['type'])
293
+ end
294
+ end
295
+
296
+ def validate_param_type(name, value, type)
297
+ case type
298
+ when 'boolean'
299
+ unless value == true || value == false
300
+ raise ArgumentError, "Parameter '#{name}' must be a boolean"
301
+ end
302
+ when 'number', 'integer', 'int'
303
+ unless value.is_a?(Numeric)
304
+ raise ArgumentError, "Parameter '#{name}' must be a number"
305
+ end
306
+ end
256
307
  end
257
308
 
258
- def ensure_version_and_distribution
309
+ def ensure_version
259
310
  return if @version
260
311
 
261
312
  response = connection.get('/')
@@ -265,26 +316,27 @@ module Antbird
265
316
 
266
317
  version_hash = response.body.dig('version')
267
318
  @version = version_hash['number']
268
- @distribution = version_hash['distribution']
269
319
  end
270
320
 
271
321
  def class_version
272
322
  version.split('.')[0, 2].join('_')
273
323
  end
274
324
 
325
+ SUPPORTED_MAJOR_VERSION = 3
326
+
275
327
  def ensure_api_spec_loaded
276
328
  return if api_specs_loaded?
277
329
 
278
- ensure_version_and_distribution
330
+ ensure_version
279
331
 
280
- if opensearch?
281
- require "antbird/rest_api/rest_api_opensearch_v#{class_version}"
282
- extend Antbird::RestApi.const_get "RestApiOpensearchV#{class_version}"
283
- else
284
- require "antbird/rest_api/rest_api_v#{class_version}"
285
- extend Antbird::RestApi.const_get "RestApiV#{class_version}"
332
+ major = version.split('.').first.to_i
333
+ unless major == SUPPORTED_MAJOR_VERSION
334
+ raise "Unsupported OpenSearch version: #{version}. Antbird currently supports OpenSearch #{SUPPORTED_MAJOR_VERSION}.x only."
286
335
  end
287
336
 
337
+ require "antbird/rest_api/rest_api_opensearch_v#{class_version}"
338
+ extend Antbird::RestApi.const_get "RestApiOpensearchV#{class_version}"
339
+
288
340
  @api_specs_loaded = true
289
341
  end
290
342