elastomer-client 3.0.1 → 3.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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +4 -0
  4. data/lib/elastomer/client/bulk.rb +1 -1
  5. data/lib/elastomer/client/cluster.rb +10 -22
  6. data/lib/elastomer/client/docs.rb +46 -55
  7. data/lib/elastomer/client/index.rb +33 -33
  8. data/lib/elastomer/client/multi_percolate.rb +9 -9
  9. data/lib/elastomer/client/multi_search.rb +5 -5
  10. data/lib/elastomer/client/native_delete_by_query.rb +2 -22
  11. data/lib/elastomer/client/nodes.rb +8 -22
  12. data/lib/elastomer/client/percolator.rb +5 -5
  13. data/lib/elastomer/client/repository.rb +7 -7
  14. data/lib/elastomer/client/rest_api_spec/api_spec.rb +119 -0
  15. data/lib/elastomer/client/rest_api_spec/api_spec_v2_3.rb +2232 -0
  16. data/lib/elastomer/client/rest_api_spec/api_spec_v2_4.rb +2250 -0
  17. data/lib/elastomer/client/rest_api_spec/api_spec_v5_6.rb +2491 -0
  18. data/lib/elastomer/client/rest_api_spec/rest_api.rb +59 -0
  19. data/lib/elastomer/client/rest_api_spec.rb +44 -0
  20. data/lib/elastomer/client/scroller.rb +10 -10
  21. data/lib/elastomer/client/snapshot.rb +7 -7
  22. data/lib/elastomer/client/tasks.rb +45 -51
  23. data/lib/elastomer/client/template.rb +8 -8
  24. data/lib/elastomer/client/warmer.rb +11 -4
  25. data/lib/elastomer/client.rb +31 -7
  26. data/lib/elastomer/version.rb +1 -1
  27. data/lib/elastomer/version_support.rb +35 -46
  28. data/script/generate-rest-api-spec +152 -0
  29. data/test/client/rest_api_spec/api_spec_test.rb +55 -0
  30. data/test/client/rest_api_spec/rest_api_test.rb +96 -0
  31. data/test/client/stubbed_client_test.rb +1 -19
  32. data/test/middleware/opaque_id_test.rb +1 -3
  33. data/test/notifications_test.rb +0 -5
  34. data/test/test_helper.rb +5 -4
  35. data/test/version_support_test.rb +3 -21
  36. metadata +13 -2
@@ -0,0 +1,59 @@
1
+ require "forwardable"
2
+
3
+ module Elastomer::Client::RestApiSpec
4
+ class RestApi
5
+ extend Forwardable
6
+
7
+ attr_reader :documentation
8
+ attr_reader :methods
9
+ attr_reader :url
10
+ attr_reader :body
11
+
12
+ def_delegators :@url,
13
+ :select_parts, :select_params, :valid_part?, :valid_param?
14
+
15
+ def initialize(documentation:, methods:, url:, body: nil)
16
+ @documentation = documentation
17
+ @methods = Array(methods)
18
+ @url = Url.new(url)
19
+ @body = body
20
+ end
21
+
22
+ def body?
23
+ !body.nil?
24
+ end
25
+
26
+ class Url
27
+ attr_reader :path
28
+ attr_reader :paths
29
+ attr_reader :parts
30
+ attr_reader :params
31
+
32
+ def initialize(path:, paths: [], parts: {}, params: {})
33
+ @path = path
34
+ @paths = Array(paths)
35
+ @parts = parts
36
+ @params = params
37
+
38
+ @parts_set = Set.new(@parts.keys)
39
+ @params_set = Set.new(@params.keys)
40
+ end
41
+
42
+ def select_parts(from:)
43
+ from.select {|k,v| valid_part?(k)}
44
+ end
45
+
46
+ def valid_part?(part)
47
+ @parts_set.include?(part.to_s)
48
+ end
49
+
50
+ def select_params(from:)
51
+ from.select {|k,v| valid_param?(k)}
52
+ end
53
+
54
+ def valid_param?(param)
55
+ @params_set.include?(param.to_s)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,44 @@
1
+
2
+ module Elastomer
3
+ class Client
4
+
5
+ # Provides access to the versioned REST API specs for Elasticsearch.
6
+ module RestApiSpec
7
+
8
+ # Returns an ApiSpec instance for the given Elasticsearcion version. This
9
+ # method will load the ApiSpec version class if it has not already been
10
+ # defined. This prevents bloat by only loading the version specs that are
11
+ # needed.
12
+ #
13
+ # Because of this lazy loading, this method is _not_ thread safe.
14
+ #
15
+ # version - the Elasticsearch version String
16
+ #
17
+ # Returns the requested ApiSpec version if available
18
+ def self.api_spec(version)
19
+ classname = "ApiSpecV#{to_class_version(version)}"
20
+ load_api_spec(version) if !self.const_defined? classname
21
+ self.const_get(classname).new
22
+ end
23
+
24
+ # Internal: Load the specific ApiSpec version class for the given version.
25
+ def self.load_api_spec(version)
26
+ path = File.expand_path("../rest_api_spec/api_spec_v#{to_class_version(version)}.rb", __FILE__)
27
+ if File.exist? path
28
+ load path
29
+ else
30
+ raise RuntimeError, "Unsupported REST API spec version: #{version}"
31
+ end
32
+ end
33
+
34
+ # Internal: Convert a dotted version String into an underscore format
35
+ # suitable for use in Ruby class names.
36
+ def self.to_class_version(version)
37
+ version.to_s.split(".").slice(0,2).join("_")
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ require_relative "rest_api_spec/api_spec"
44
+ require_relative "rest_api_spec/rest_api"
@@ -12,7 +12,7 @@ module Elastomer
12
12
  #
13
13
  # Examples
14
14
  #
15
- # scroll = client.scroll('{"query":{"match_all":{}}}', :index => 'test')
15
+ # scroll = client.scroll('{"query":{"match_all":{}}}', index: 'test')
16
16
  # scroll.each_document do |document|
17
17
  # document['_id']
18
18
  # document['_source']
@@ -35,7 +35,7 @@ module Elastomer
35
35
  #
36
36
  # Examples
37
37
  #
38
- # scan = client.scan('{"query":{"match_all":{}}}', :index => 'test')
38
+ # scan = client.scan('{"query":{"match_all":{}}}', index: 'test')
39
39
  # scan.each_document do |document|
40
40
  # document['_id']
41
41
  # document['_source']
@@ -59,7 +59,7 @@ module Elastomer
59
59
  #
60
60
  # Examples
61
61
  #
62
- # h = client.start_scroll(:body => '{"query":{"match_all":{}},"sort":{"created":"desc"}}', :index => 'test')
62
+ # h = client.start_scroll(body: '{"query":{"match_all":{}},"sort":{"created":"desc"}}', index: 'test')
63
63
  # scroll_id = h['_scroll_id']
64
64
  # h['hits']['hits'].each { |doc| ... }
65
65
  #
@@ -71,7 +71,7 @@ module Elastomer
71
71
  #
72
72
  # Returns the response body as a Hash.
73
73
  def start_scroll( opts = {} )
74
- opts = opts.merge :action => "search.start_scroll"
74
+ opts = opts.merge action: "search.start_scroll", rest_api: "search"
75
75
  response = get "{/index}{/type}/_search", opts
76
76
  response.body
77
77
  end
@@ -84,7 +84,7 @@ module Elastomer
84
84
  #
85
85
  # Examples
86
86
  #
87
- # scroll_id = client.start_scroll(:body => '{"query":{"match_all":{}}}', :index => 'test')['_scroll_id']
87
+ # scroll_id = client.start_scroll(body: '{"query":{"match_all":{}}}', index: 'test')['_scroll_id']
88
88
  #
89
89
  # h = client.continue_scroll scroll_id # scroll to get the next set of results
90
90
  # scroll_id = h['_scroll_id'] # and store the scroll_id to use later
@@ -96,7 +96,7 @@ module Elastomer
96
96
  #
97
97
  # Returns the response body as a Hash.
98
98
  def continue_scroll( scroll_id, scroll = "5m" )
99
- response = get "/_search/scroll", :body => {:scroll_id => scroll_id}, :scroll => scroll, :action => "search.scroll"
99
+ response = get "/_search/scroll", body: {scroll_id: scroll_id}, scroll: scroll, action: "search.scroll", rest_api: "scroll"
100
100
  response.body
101
101
  rescue RequestError => err
102
102
  if err.error && err.error["caused_by"]["type"] == "search_context_missing_exception"
@@ -113,7 +113,7 @@ module Elastomer
113
113
  #
114
114
  # Returns the response body as a Hash.
115
115
  def clear_scroll( scroll_ids )
116
- response = delete "/_search/scroll", :body => {scroll_id: Array(scroll_ids)}, :action => "search.clear_scroll"
116
+ response = delete "/_search/scroll", body: {scroll_id: Array(scroll_ids)}, action: "search.clear_scroll", rest_api: "clear_scroll"
117
117
  response.body
118
118
  end
119
119
 
@@ -132,7 +132,7 @@ module Elastomer
132
132
  raise ArgumentError, "Query cannot contain a sort (found sort '#{query[:sort]}' in query: #{query})"
133
133
  end
134
134
 
135
- query.merge(:sort => [:_doc])
135
+ query.merge(sort: [:_doc])
136
136
  end
137
137
 
138
138
  DEFAULT_OPTS = {
@@ -161,7 +161,7 @@ module Elastomer
161
161
  #
162
162
  # Examples
163
163
  #
164
- # scan = Scroller.new(client, {:query => {:match_all => {}}}, :index => 'test-1')
164
+ # scan = Scroller.new(client, {query: {match_all: {}}}, index: 'test-1')
165
165
  # scan.each_document { |doc|
166
166
  # doc['_id']
167
167
  # doc['_source']
@@ -170,7 +170,7 @@ module Elastomer
170
170
  def initialize( client, query, opts = {} )
171
171
  @client = client
172
172
 
173
- @opts = DEFAULT_OPTS.merge({ :body => query }).merge(opts)
173
+ @opts = DEFAULT_OPTS.merge({ body: query }).merge(opts)
174
174
 
175
175
  @scroll_id = nil
176
176
  @offset = 0
@@ -35,7 +35,7 @@ module Elastomer
35
35
  #
36
36
  # Returns true if the snapshot exists
37
37
  def exists?(params = {})
38
- response = client.get "/_snapshot/{repository}/{snapshot}", update_params(params, :action => "snapshot.exists")
38
+ response = client.get "/_snapshot/{repository}/{snapshot}", update_params(params, action: "snapshot.exists", rest_api: "snapshot.get")
39
39
  response.success?
40
40
  rescue Elastomer::Client::Error => err
41
41
  if err.error && err.error.dig("root_cause", 0, "type") == "snapshot_missing_exception"
@@ -54,7 +54,7 @@ module Elastomer
54
54
  #
55
55
  # Returns the response body as a Hash
56
56
  def create(body = {}, params = {})
57
- response = client.put "/_snapshot/{repository}/{snapshot}", update_params(params, :body => body, :action => "snapshot.create")
57
+ response = client.put "/_snapshot/{repository}/{snapshot}", update_params(params, body: body, action: "snapshot.create", rest_api: "snapshot.create")
58
58
  response.body
59
59
  end
60
60
 
@@ -67,7 +67,7 @@ module Elastomer
67
67
  def get(params = {})
68
68
  # Set snapshot name or we'll get the repository instead
69
69
  snapshot = name || "_all"
70
- response = client.get "/_snapshot/{repository}/{snapshot}", update_params(params, :snapshot => snapshot, :action => "snapshot.get")
70
+ response = client.get "/_snapshot/{repository}/{snapshot}", update_params(params, snapshot: snapshot, action: "snapshot.get", rest_api: "snapshot.get")
71
71
  response.body
72
72
  end
73
73
 
@@ -78,7 +78,7 @@ module Elastomer
78
78
  #
79
79
  # Returns the response body as a Hash
80
80
  def status(params = {})
81
- response = client.get "/_snapshot{/repository}{/snapshot}/_status", update_params(params, :action => "snapshot.status")
81
+ response = client.get "/_snapshot{/repository}{/snapshot}/_status", update_params(params, action: "snapshot.status", rest_api: "snapshot.status")
82
82
  response.body
83
83
  end
84
84
 
@@ -90,7 +90,7 @@ module Elastomer
90
90
  #
91
91
  # Returns the response body as a Hash
92
92
  def restore(body = {}, params = {})
93
- response = client.post "/_snapshot/{repository}/{snapshot}/_restore", update_params(params, :body => body, :action => "snapshot.restore")
93
+ response = client.post "/_snapshot/{repository}/{snapshot}/_restore", update_params(params, body: body, action: "snapshot.restore", rest_api: "snapshot.restore")
94
94
  response.body
95
95
  end
96
96
 
@@ -101,7 +101,7 @@ module Elastomer
101
101
  #
102
102
  # Returns the response body as a Hash
103
103
  def delete(params = {})
104
- response = client.delete "/_snapshot/{repository}/{snapshot}", update_params(params, :action => "snapshot.delete")
104
+ response = client.delete "/_snapshot/{repository}/{snapshot}", update_params(params, action: "snapshot.delete", rest_api: "snapshot.delete")
105
105
  response.body
106
106
  end
107
107
 
@@ -120,7 +120,7 @@ module Elastomer
120
120
 
121
121
  # Internal: Returns a Hash containing default parameters.
122
122
  def defaults
123
- { :repository => repository, :snapshot => name }
123
+ { repository: repository, snapshot: name }
124
124
  end
125
125
  end
126
126
  end
@@ -12,18 +12,6 @@ module Elastomer
12
12
 
13
13
  class Tasks
14
14
 
15
- # TODO - validate params from this whitelist
16
- PARAMETERS = %i[
17
- nodes
18
- actions
19
- parent_task_id
20
- wait_for_completion
21
- pretty
22
- detailed
23
- timeout
24
- group_by
25
- ].to_set.freeze
26
-
27
15
  # Create a new Tasks for introspecting on internal cluster activity.
28
16
  # More context: https://www.elastic.co/guide/en/elasticsearch/reference/5.6/tasks.html
29
17
  #
@@ -38,21 +26,21 @@ module Elastomer
38
26
 
39
27
  # Fetch results from the generic _tasks endpoint.
40
28
  #
41
- # params - Hash of request parameters, including:
29
+ # params - Hash of request parameters, including:
42
30
  #
43
31
  # Examples
44
32
  #
45
33
  # tasks.get
46
- # tasks.get :nodes => "DmteLdw1QmSgW3GZmjmoKA,DmteLdw1QmSgW3GZmjmoKB", :actions => "cluster:*", :detailed => true
34
+ # tasks.get nodes: "DmteLdw1QmSgW3GZmjmoKA,DmteLdw1QmSgW3GZmjmoKB", actions: "cluster:*", detailed: true
47
35
  #
48
36
  # Examples (ES 5+ only)
49
37
  #
50
- # tasks.get :group_by => "parents"
51
- # tasks.get :group_by => "parents", :actions => "*reindex", ...
38
+ # tasks.get group_by: "parents"
39
+ # tasks.get group_by: "parents", actions: "*reindex", ...
52
40
  #
53
41
  # Returns the response body as a Hash
54
42
  def get(params = {})
55
- response = client.get "/_tasks", params
43
+ response = client.get "/_tasks", params.merge(action: "tasks.list", rest_api: "tasks.list")
56
44
  response.body
57
45
  end
58
46
 
@@ -61,22 +49,24 @@ module Elastomer
61
49
  # where "node_id" is a value from the "nodes" hash returned from the /_tasks endpoint, and "task_id" is
62
50
  # from the "tasks" child hash of any of the "nodes" entries of the /_tasks endpoint
63
51
  #
64
- # node_id - the name of the ES cluster node hosting the target task
65
- # task_id - the numerical ID of the task to return data about in the response
66
- # params - Hash of request parameters to include
52
+ # node_id - the name of the ES cluster node hosting the target task
53
+ # task_id - the numerical ID of the task to return data about in the response
54
+ # params - Hash of request parameters to include
67
55
  #
68
56
  # Examples
69
57
  #
70
58
  # tasks.get_by_id "DmteLdw1QmSgW3GZmjmoKA", 123
71
- # tasks.get_by_id "DmteLdw1QmSgW3GZmjmoKA", 456, :pretty => true
59
+ # tasks.get_by_id "DmteLdw1QmSgW3GZmjmoKA", 456, pretty: true
72
60
  #
73
61
  # Returns the response body as a Hash
74
62
  def get_by_id(node_id, task_id, params = {})
75
63
  raise ArgumentError, "invalid node ID provided: #{node_id.inspect}" if node_id.to_s.empty?
76
64
  raise ArgumentError, "invalid task ID provided: #{task_id.inspect}" unless task_id.is_a?(Integer)
77
65
 
66
+ rest_api = client.version_support.supports_tasks_get? ? "tasks.get" : "tasks.list"
67
+
78
68
  # in this API, the task ID is included in the path, not as a request parameter.
79
- response = client.get "/_tasks/#{node_id}:#{task_id}", params
69
+ response = client.get "/_tasks/{task_id}", params.merge(task_id: "#{node_id}:#{task_id}", action: "tasks.get", rest_api: rest_api)
80
70
  response.body
81
71
  end
82
72
 
@@ -85,9 +75,9 @@ module Elastomer
85
75
  # is not the correct syntax for the parent_task_id param value. The correct
86
76
  # value syntax is "<parent_node_id>:<parent_task_id>"
87
77
  #
88
- # parent_node_id - ID of the node the parent task is hosted by
89
- # parent_task_id - ID of a parent task who's child tasks' data will be returned in the response
90
- # params - Hash of request parameters to include
78
+ # parent_node_id - ID of the node the parent task is hosted by
79
+ # parent_task_id - ID of a parent task who's child tasks' data will be returned in the response
80
+ # params - Hash of request parameters to include
91
81
  #
92
82
  # Examples
93
83
  #
@@ -99,50 +89,55 @@ module Elastomer
99
89
  raise ArgumentError, "invalid parent node ID provided: #{parent_node_id.inspect}" if node_id.to_s.empty?
100
90
  raise ArgumentError, "invalid parent task ID provided: #{parent_task_id.inspect}" unless parent_task_id.is_a?(Integer)
101
91
 
102
- # in this API, we pass the parent task ID as a formatted parameter in a request to the _tasks endpoint
103
- formatted_parent = { :parent_task_id => "#{parent_node_id}:#{parent_task_id}" }
104
- response = client.get "/_tasks", params.merge(formatted_parent)
92
+ parent_task_id = "#{parent_node_id}:#{parent_task_id}"
93
+ params = params.merge(action: "tasks.parent", rest_api: "tasks.list")
94
+
95
+ if client.version_support.supports_parent_task_id?
96
+ params[:parent_task_id] = parent_task_id
97
+ else
98
+ params[:parent_task] = parent_task_id
99
+ end
100
+
101
+ response = client.get "/_tasks", params
105
102
  response.body
106
103
  end
107
104
 
108
105
  # Wait for the specified amount of time (10 seconds by default) for some task(s) to complete.
109
106
  # Filters for task(s) to wait upon using same filter params as Tasks#get(params)
110
107
  #
111
- # timeout - maximum time to wait for target task to complete before returning, example: "5s"
112
- # params - Hash of request params to include (mostly task filters in this context)
108
+ # timeout - maximum time to wait for target task to complete before returning, example: "5s"
109
+ # params - Hash of request params to include (mostly task filters in this context)
113
110
  #
114
111
  # Examples
115
112
  #
116
- # tasks.wait_for "5s", :actions => "*health"
117
- # tasks.wait_for("30s", :actions => "*reindex", :nodes => "DmteLdw1QmSgW3GZmjmoKA,DmteLdw1QmSgW3GZmjmoKB")
113
+ # tasks.wait_for "5s", actions: "*health"
114
+ # tasks.wait_for("30s", actions: "*reindex", nodes: "DmteLdw1QmSgW3GZmjmoKA,DmteLdw1QmSgW3GZmjmoKB")
118
115
  #
119
116
  # Returns the response body as a Hash when timeout expires or target tasks complete
120
117
  # COMPATIBILITY WARNING: the response body differs between ES versions for this API
121
118
  def wait_for(timeout = "10s", params = {})
122
- params_with_wait = params.merge({ :wait_for_completion => true, :timeout => timeout })
123
- self.get(params_with_wait)
119
+ self.get params.merge(wait_for_completion: true, timeout: timeout)
124
120
  end
125
121
 
126
122
  # Wait for the specified amount of time (10 seconds by default) for some task(s) to complete.
127
123
  # Filters for task(s) to wait upon using same IDs and filter params as Tasks#get_by_id(params)
128
124
  #
129
- # node_id - the ID of the node on which the target task is hosted
130
- # task_id - the ID of the task to wait on
131
- # timeout - time for call to await target tasks completion before returning
132
- # params - Hash of request params to include (mostly task filters in this context)
125
+ # node_id - the ID of the node on which the target task is hosted
126
+ # task_id - the ID of the task to wait on
127
+ # timeout - time for call to await target tasks completion before returning
128
+ # params - Hash of request params to include (mostly task filters in this context)
133
129
  #
134
130
  # Examples
135
131
  #
136
132
  # tasks.wait_by_id "DmteLdw1QmSgW3GZmjmoKA", 123, "15s"
137
- # tasks.wait_by_id "DmteLdw1QmSgW3GZmjmoKA", 456, "30s", :actions => "*search"
133
+ # tasks.wait_by_id "DmteLdw1QmSgW3GZmjmoKA", 456, "30s", actions: "*search"
138
134
  #
139
135
  # Returns the response body as a Hash when timeout expires or target tasks complete
140
136
  def wait_by_id(node_id, task_id, timeout = "10s", params = {})
141
137
  raise ArgumentError, "invalid node ID provided: #{node_id.inspect}" if node_id.to_s.empty?
142
138
  raise ArgumentError, "invalid task ID provided: #{task_id.inspect}" unless task_id.is_a?(Integer)
143
139
 
144
- params_with_wait = params.merge({ :wait_for_completion => true, :timeout => timeout })
145
- self.get_by_id(node_id, task_id, params_with_wait)
140
+ self.get_by_id(node_id, task_id, params.merge(wait_for_completion: true, timeout: timeout))
146
141
  end
147
142
 
148
143
  # Cancels a task running on a particular node.
@@ -157,32 +152,31 @@ module Elastomer
157
152
  # Examples
158
153
  #
159
154
  # tasks.cancel_by_id "DmteLdw1QmSgW3GZmjmoKA", 123
160
- # tasks.cancel_by_id "DmteLdw1QmSgW3GZmjmoKA", 456, :pretty => true
155
+ # tasks.cancel_by_id "DmteLdw1QmSgW3GZmjmoKA", 456, pretty: true
161
156
  #
162
157
  # Returns the response body as a Hash
163
158
  def cancel_by_id(node_id, task_id, params = {})
164
159
  raise ArgumentError, "invalid node ID provided: #{node_id.inspect}" if node_id.to_s.empty?
165
160
  raise ArgumentError, "invalid task ID provided: #{task_id.inspect}" unless task_id.is_a?(Integer)
166
161
 
167
- response = client.post "/_tasks/#{node_id}:#{task_id}/_cancel", params
168
- response.body
162
+ self.cancel(params.merge(task_id: "#{node_id}:#{task_id}"))
169
163
  end
170
164
 
171
165
  # Cancels a task or group of tasks using various filtering parameters.
172
166
  #
173
- # params - Hash of request parameters to include
167
+ # params - Hash of request parameters to include
174
168
  #
175
169
  # Examples
176
170
  #
177
- # tasks.cancel :actions => "*reindex"
178
- # tasks.cancel :actions => "*search", :nodes => "DmteLdw1QmSgW3GZmjmoKA,DmteLdw1QmSgW3GZmjmoKB,DmteLdw1QmSgW3GZmjmoKC"
171
+ # tasks.cancel actions: "*reindex"
172
+ # tasks.cancel actions: "*search", nodes: "DmteLdw1QmSgW3GZmjmoKA,DmteLdw1QmSgW3GZmjmoKB,DmteLdw1QmSgW3GZmjmoKC"
179
173
  #
180
174
  # Returns the response body as a Hash
181
175
  def cancel(params = {})
182
- response = client.post "/_tasks/_cancel", params
176
+ response = client.post "/_tasks{/task_id}/_cancel", params.merge(action: "tasks.cancel", rest_api: "tasks.cancel")
183
177
  response.body
184
178
  end
185
179
 
186
- end # end class Tasks
187
- end # end class Client
188
- end # end module Elastomer
180
+ end
181
+ end
182
+ end
@@ -25,7 +25,7 @@ module Elastomer
25
25
 
26
26
  # Returns true if the template already exists on the cluster.
27
27
  def exists?( params = {} )
28
- response = client.head "/_template/{template}", update_params(params, action: "template.exists")
28
+ response = client.head "/_template/{template}", update_params(params, action: "template.exists", rest_api: "indices.exists_template")
29
29
  response.success?
30
30
  end
31
31
  alias_method :exist?, :exists?
@@ -37,7 +37,7 @@ module Elastomer
37
37
  #
38
38
  # Returns the response body as a Hash
39
39
  def get( params = {} )
40
- response = client.get "/_template/{template}", update_params(params, :action => "template.get")
40
+ response = client.get "/_template/{template}", update_params(params, action: "template.get", rest_api: "indices.get_template")
41
41
  response.body
42
42
  end
43
43
 
@@ -49,7 +49,7 @@ module Elastomer
49
49
  #
50
50
  # Returns the response body as a Hash
51
51
  def create( template, params = {} )
52
- response = client.put "/_template/{template}", update_params(params, :body => template, :action => "template.create")
52
+ response = client.put "/_template/{template}", update_params(params, body: template, action: "template.create", rest_api: "indices.put_template")
53
53
  response.body
54
54
  end
55
55
 
@@ -60,7 +60,7 @@ module Elastomer
60
60
  #
61
61
  # Returns the response body as a Hash
62
62
  def delete( params = {} )
63
- response = client.delete "/_template/{template}", update_params(params, :action => "template.delete")
63
+ response = client.delete "/_template/{template}", update_params(params, action: "template.delete", rest_api: "indices.delete_template")
64
64
  response.body
65
65
  end
66
66
 
@@ -79,8 +79,8 @@ module Elastomer
79
79
 
80
80
  # Internal: Returns a Hash containing default parameters.
81
81
  def defaults
82
- { :template => name }
82
+ { template: name }
83
83
  end
84
- end # Template
85
- end # Client
86
- end # Elastomer
84
+ end
85
+ end
86
+ end
@@ -34,7 +34,7 @@ module Elastomer
34
34
  #
35
35
  # Returns the response body as a Hash
36
36
  def create(query, params = {})
37
- response = client.put "/{index}{/type}/_warmer/{warmer}", defaults.update(params.update(:body => query))
37
+ response = client.put "/{index}{/type}/_warmer/{warmer}", update_params(params, body: query, action: "warmer.create", rest_api: "indices.put_warmer")
38
38
  response.body
39
39
  end
40
40
 
@@ -45,7 +45,7 @@ module Elastomer
45
45
  #
46
46
  # Returns the response body as a Hash
47
47
  def delete(params = {})
48
- response = client.delete "/{index}{/type}/_warmer/{warmer}", defaults.update(params)
48
+ response = client.delete "/{index}{/type}/_warmer/{warmer}", update_params(params, action: "warmer.delete", rest_api: "indices.delete_warmer")
49
49
  response.body
50
50
  end
51
51
 
@@ -56,7 +56,7 @@ module Elastomer
56
56
  #
57
57
  # Returns the response body as a Hash
58
58
  def get(params = {})
59
- response = client.get "/{index}{/type}/_warmer/{warmer}", defaults.update(params)
59
+ response = client.get "/{index}{/type}/_warmer/{warmer}", update_params(params, action: "warmer.get", rest_api: "indices.get_warmer")
60
60
  response.body
61
61
  end
62
62
 
@@ -82,9 +82,16 @@ module Elastomer
82
82
  end
83
83
  alias_method :exist?, :exists?
84
84
 
85
+ # Internal:
86
+ def update_params(params, overrides = nil)
87
+ h = defaults.update params
88
+ h.update overrides unless overrides.nil?
89
+ h
90
+ end
91
+
85
92
  # Internal: Returns a Hash containing default parameters.
86
93
  def defaults
87
- {:index => index_name, :warmer => name}
94
+ {index: index_name, warmer: name}
88
95
  end
89
96
  end
90
97
  end
@@ -31,10 +31,12 @@ module Elastomer
31
31
  # :max_request_size - the maximum allowed request size in bytes (defaults to 250 MB)
32
32
  # :max_retries - the maximum number of request retires (defaults to 0)
33
33
  # :retry_delay - delay in seconds between retries (defaults to 0.075)
34
+ # :strict_params - set to `true` to raise exceptions when invalid request params are used
34
35
  #
35
36
  def initialize(host: "localhost", port: 9200, url: nil,
36
37
  read_timeout: 5, open_timeout: 2, max_retries: 0, retry_delay: 0.075,
37
- opaque_id: false, adapter: Faraday.default_adapter, max_request_size: MAX_REQUEST_SIZE)
38
+ opaque_id: false, adapter: Faraday.default_adapter, max_request_size: MAX_REQUEST_SIZE,
39
+ strict_params: false)
38
40
 
39
41
  @url = url || "http://#{host}:#{port}"
40
42
 
@@ -49,11 +51,14 @@ module Elastomer
49
51
  @adapter = adapter
50
52
  @opaque_id = opaque_id
51
53
  @max_request_size = max_request_size
54
+ @strict_params = strict_params
52
55
  end
53
56
 
54
57
  attr_reader :host, :port, :url
55
58
  attr_reader :read_timeout, :open_timeout
56
59
  attr_reader :max_retries, :retry_delay, :max_request_size
60
+ attr_reader :strict_params
61
+ alias :strict_params? :strict_params
57
62
 
58
63
  # Returns a duplicate of this Client connection configured in the exact same
59
64
  # fashion.
@@ -69,7 +74,7 @@ module Elastomer
69
74
 
70
75
  # Returns true if the server is available; returns false otherwise.
71
76
  def ping
72
- response = head "/", :action => "cluster.ping"
77
+ response = head "/", action: "cluster.ping"
73
78
  response.success?
74
79
  rescue StandardError
75
80
  false
@@ -78,7 +83,10 @@ module Elastomer
78
83
 
79
84
  # Returns the version String of the attached Elasticsearch instance.
80
85
  def version
81
- @version ||= info["version"]["number"]
86
+ @version ||= begin
87
+ response = get "/"
88
+ response.body.dig("version", "number")
89
+ end
82
90
  end
83
91
 
84
92
  # Returns a Semantic::Version for the attached Elasticsearch instance.
@@ -89,10 +97,16 @@ module Elastomer
89
97
 
90
98
  # Returns the information Hash from the attached Elasticsearch instance.
91
99
  def info
92
- response = get "/", :action => "cluster.info"
100
+ response = get "/", action: "cluster.info"
93
101
  response.body
94
102
  end
95
103
 
104
+ # Returns the ApiSpec for the specific version of Elasticsearch that this
105
+ # Client is connected to.
106
+ def api_spec
107
+ @api_spec ||= RestApiSpec.api_spec(version)
108
+ end
109
+
96
110
  # Internal: Provides access to the Faraday::Connection used by this client
97
111
  # for all requests to the server.
98
112
  #
@@ -102,7 +116,7 @@ module Elastomer
102
116
  conn.request(:encode_json)
103
117
  conn.response(:parse_json)
104
118
  conn.request(:opaque_id) if @opaque_id
105
- conn.request(:limit_size, :max_request_size => max_request_size) if max_request_size
119
+ conn.request(:limit_size, max_request_size: max_request_size) if max_request_size
106
120
 
107
121
  if @adapter.is_a?(Array)
108
122
  conn.adapter(*@adapter)
@@ -307,10 +321,10 @@ module Elastomer
307
321
  #
308
322
  # Examples
309
323
  #
310
- # expand_path('/foo{/bar}', {:bar => 'hello', :q => 'what', :p => 2})
324
+ # expand_path('/foo{/bar}', {bar: 'hello', q: 'what', p: 2})
311
325
  # #=> '/foo/hello?q=what&p=2'
312
326
  #
313
- # expand_path('/foo{/bar}{/baz}', {:baz => 'no bar'}
327
+ # expand_path('/foo{/bar}{/baz}', {baz: 'no bar'}
314
328
  # #=> '/foo/no%20bar'
315
329
  #
316
330
  # Returns an Addressable::Uri
@@ -323,12 +337,22 @@ module Elastomer
323
337
  query_values.delete :context
324
338
  query_values.delete :retries
325
339
 
340
+ rest_api = query_values.delete :rest_api
341
+
326
342
  template.keys.map(&:to_sym).each do |key|
327
343
  value = query_values.delete key
328
344
  value = assert_param_presence(value, key) unless path =~ /{\/#{key}}/ && value.nil?
329
345
  expansions[key] = value
330
346
  end
331
347
 
348
+ if rest_api
349
+ query_values = if strict_params?
350
+ api_spec.validate_params!(api: rest_api, params: query_values)
351
+ else
352
+ api_spec.select_params(api: rest_api, from: query_values)
353
+ end
354
+ end
355
+
332
356
  uri = template.expand(expansions)
333
357
  uri.query_values = query_values unless query_values.empty?
334
358
  uri.to_s
@@ -1,5 +1,5 @@
1
1
  module Elastomer
2
- VERSION = "3.0.1"
2
+ VERSION = "3.1.0"
3
3
 
4
4
  def self.version
5
5
  VERSION