elastomer-client 3.0.1 → 3.1.0

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