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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 149f340281bffddc3fac2a8a783890725da2ee30
4
- data.tar.gz: e25c7f445e3002c6c751fca4119e7abc6311885b
3
+ metadata.gz: 2d22300f2c0a799c8b2253f60928f24d7da61e39
4
+ data.tar.gz: 19e135b931dece3946b68469ad52f56dcb9b8271
5
5
  SHA512:
6
- metadata.gz: b738a0e0c1ee712fcdd2143996415b419203fc55c692e38d0ccc8c44caa0795f843e8da880a6ea4b4d346f65bdfbfcd6cd45c57db7d7fa00387c467cb0d3c36f
7
- data.tar.gz: 3622c6b76c88e224f38587acd480f3bb2875abfe15e2a29ba7c00c1ea12c1166c1ef8522d93425e1a44b078f9a0d8515446a4682a4923787cb165a03c2f75228
6
+ metadata.gz: cebdfe02683dee2502e7beb39040dc876884d44656b2f0f5a0aa071621cd6c89be23a39c88b2b0185bc1035c76f01dd42748e1b5cdd14a13ecf5fa1c93696b7f
7
+ data.tar.gz: 06bbcedac35ee8a1a947b3fcb87c6e451d05d03f390971f5de678c3298b1924414e22cc062459ef378d872134a8d36b5f92cbb903d9c35b7aa923f8055b1d977
data/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
1
  /bin
2
+ /vendor/elasticsearch
2
3
  /vendor/gems
3
4
  /.bundle
4
5
  /.rbenv-version
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 3.1.0 (2018-01-19)
2
+ - Added the `strict_params` flag for enforcing params passed to the REST API
3
+ - Added the `RestApiSpec` module and classes for enforcing strict params
4
+
1
5
  ## 3.0.1 (2017-12-20)
2
6
  - Fixed argument passing to `app_delete_by_query`
3
7
  - Explicitly close scroll search contexts when scroll is complete
@@ -38,7 +38,7 @@ module Elastomer
38
38
  raise "bulk request body cannot be nil" if body.nil?
39
39
  params ||= {}
40
40
 
41
- response = self.post "{/index}{/type}/_bulk", params.merge(:body => body, :action => "bulk")
41
+ response = self.post "{/index}{/type}/_bulk", params.merge(body: body, action: "bulk", rest_api: "bulk")
42
42
  response.body
43
43
  end
44
44
  end
@@ -34,7 +34,7 @@ module Elastomer
34
34
  #
35
35
  # Returns the response as a Hash
36
36
  def health( params = {} )
37
- response = client.get "/_cluster/health{/index}", params.merge(action: "cluster.health")
37
+ response = client.get "/_cluster/health{/index}", params.merge(action: "cluster.health", rest_api: "cluster.health")
38
38
  response.body
39
39
  end
40
40
 
@@ -52,7 +52,7 @@ module Elastomer
52
52
  #
53
53
  # Returns the response as a Hash
54
54
  def state( params = {} )
55
- response = client.get "/_cluster/state{/metrics}{/indices}", params.merge(action: "cluster.state")
55
+ response = client.get "/_cluster/state{/metrics}{/indices}", params.merge(action: "cluster.state", rest_api: "cluster.state")
56
56
  response.body
57
57
  end
58
58
 
@@ -67,7 +67,7 @@ module Elastomer
67
67
  #
68
68
  # Returns the response as a Hash
69
69
  def stats( params = {} )
70
- response = client.get "/_cluster/stats", params.merge(action: "cluster.stats")
70
+ response = client.get "/_cluster/stats", params.merge(action: "cluster.stats", rest_api: "cluster.stats")
71
71
  response.body
72
72
  end
73
73
 
@@ -80,7 +80,7 @@ module Elastomer
80
80
  #
81
81
  # Returns the response as a Hash
82
82
  def pending_tasks( params = {} )
83
- response = client.get "/_cluster/pending_tasks", params.merge(action: "cluster.pending_tasks")
83
+ response = client.get "/_cluster/pending_tasks", params.merge(action: "cluster.pending_tasks", rest_api: "cluster.pending_tasks")
84
84
  response.body
85
85
  end
86
86
 
@@ -101,7 +101,7 @@ module Elastomer
101
101
  #
102
102
  # Returns the response as a Hash
103
103
  def get_settings( params = {} )
104
- response = client.get "/_cluster/settings", params.merge(action: "cluster.get_settings")
104
+ response = client.get "/_cluster/settings", params.merge(action: "cluster.get_settings", rest_api: "cluster.get_settings")
105
105
  response.body
106
106
  end
107
107
  alias_method :settings, :get_settings
@@ -117,7 +117,7 @@ module Elastomer
117
117
  #
118
118
  # Returns the response as a Hash
119
119
  def update_settings( body, params = {} )
120
- response = client.put "/_cluster/settings", params.merge(body: body, action: "cluster.update_settings")
120
+ response = client.put "/_cluster/settings", params.merge(body: body, action: "cluster.update_settings", rest_api: "cluster.put_settings")
121
121
  response.body
122
122
  end
123
123
 
@@ -157,20 +157,7 @@ module Elastomer
157
157
  body = {commands: Array(commands)}
158
158
  end
159
159
 
160
- response = client.post "/_cluster/reroute", params.merge(body: body, action: "cluster.reroute")
161
- response.body
162
- end
163
-
164
- # Shutdown the entire cluster. There is also a Nodes#shutdown method for
165
- # shutting down individual nodes.
166
- #
167
- # params - Parameters Hash
168
- #
169
- # See https://www.elastic.co/guide/en/elasticsearch/reference/current/cluster-nodes-shutdown.html
170
- #
171
- # Returns the response as a Hash
172
- def shutdown( params = {} )
173
- response = client.post "/_shutdown", params.merge(action: "cluster.shutdown")
160
+ response = client.post "/_cluster/reroute", params.merge(body: body, action: "cluster.reroute", rest_api: "cluster.reroute")
174
161
  response.body
175
162
  end
176
163
 
@@ -181,6 +168,7 @@ module Elastomer
181
168
  #
182
169
  # params - Parameters Hash
183
170
  # :index - an index name or Array of index names
171
+ # :name - an alias name or Array of alias names
184
172
  #
185
173
  # Examples
186
174
  #
@@ -191,7 +179,7 @@ module Elastomer
191
179
  #
192
180
  # Returns the response body as a Hash
193
181
  def get_aliases( params = {} )
194
- response = client.get "{/index}/_aliases", params.merge(action: "cluster.get_aliases")
182
+ response = client.get "{/index}/_alias{/name}", params.merge(action: "cluster.get_aliases", rest_api: "indices.get_alias")
195
183
  response.body
196
184
  end
197
185
  alias_method :aliases, :get_aliases
@@ -227,7 +215,7 @@ module Elastomer
227
215
  body = {actions: Array(actions)}
228
216
  end
229
217
 
230
- response = client.post "/_aliases", params.merge(body: body, action: "cluster.update_aliases")
218
+ response = client.post "/_aliases", params.merge(body: body, action: "cluster.update_aliases", rest_api: "indices.update_aliases")
231
219
  response.body
232
220
  end
233
221
 
@@ -15,7 +15,6 @@ module Elastomer
15
15
  Docs.new self, name, type
16
16
  end
17
17
 
18
-
19
18
  class Docs
20
19
  # Create a new document client for making API requests that pertain to
21
20
  # the indexing and searching of documents in a search index.
@@ -79,7 +78,7 @@ module Elastomer
79
78
  def index( document, params = {} )
80
79
  overrides = from_document document
81
80
  params = update_params(params, overrides)
82
- params[:action] = "docs.index"
81
+ params.merge!(action: "docs.index", rest_api: "index")
83
82
 
84
83
  params.delete(:id) if params[:id].nil? || params[:id].to_s =~ /\A\s*\z/
85
84
 
@@ -103,7 +102,7 @@ module Elastomer
103
102
  #
104
103
  # Returns the response body as a Hash
105
104
  def delete( params = {} )
106
- response = client.delete "/{index}/{type}/{id}", update_params(params, :action => "docs.delete")
105
+ response = client.delete "/{index}/{type}/{id}", update_params(params, action: "docs.delete", rest_api: "delete")
107
106
  response.body
108
107
  end
109
108
 
@@ -117,7 +116,7 @@ module Elastomer
117
116
  #
118
117
  # Returns the response body as a Hash
119
118
  def get( params = {} )
120
- response = client.get "/{index}/{type}/{id}", update_params(params, :action => "docs.get")
119
+ response = client.get "/{index}/{type}/{id}", update_params(params, action: "docs.get", rest_api: "get")
121
120
  response.body
122
121
  end
123
122
 
@@ -131,7 +130,7 @@ module Elastomer
131
130
  #
132
131
  # Returns true if the document exists
133
132
  def exists?( params = {} )
134
- response = client.head "/{index}/{type}/{id}", update_params(params, :action => "docs.exists")
133
+ response = client.head "/{index}/{type}/{id}", update_params(params, action: "docs.exists", rest_api: "exists")
135
134
  response.success?
136
135
  end
137
136
  alias_method :exist?, :exists?
@@ -146,7 +145,7 @@ module Elastomer
146
145
  #
147
146
  # Returns the response body as a Hash
148
147
  def source( params = {} )
149
- response = client.get "/{index}/{type}/{id}/_source", update_params(params, :action => "docs.source")
148
+ response = client.get "/{index}/{type}/{id}/_source", update_params(params, action: "docs.source", rest_api: "get_source")
150
149
  response.body
151
150
  end
152
151
 
@@ -160,7 +159,7 @@ module Elastomer
160
159
  # Returns the response body as a Hash
161
160
  def multi_get( body, params = {} )
162
161
  overrides = from_document body
163
- overrides[:action] = "docs.multi_get"
162
+ overrides.merge!(action: "docs.multi_get", rest_api: "mget")
164
163
 
165
164
  response = client.get "{/index}{/type}/_mget", update_params(params, overrides)
166
165
  response.body
@@ -177,7 +176,7 @@ module Elastomer
177
176
  # Returns the response body as a Hash
178
177
  def update( script, params = {} )
179
178
  overrides = from_document script
180
- overrides[:action] = "docs.update"
179
+ overrides.merge!(action: "docs.update", rest_api: "update")
181
180
 
182
181
  response = client.post "/{index}/{type}/{id}/_update", update_params(params, overrides)
183
182
  response.body
@@ -195,10 +194,10 @@ module Elastomer
195
194
  # Examples
196
195
  #
197
196
  # # request body query
198
- # search({:query => {:match_all => {}}}, :type => 'tweet')
197
+ # search({query: {match_all: {}}}, type: 'tweet')
199
198
  #
200
199
  # # same thing but using the URI request method
201
- # search(:q => '*:*', :type => 'tweet')
200
+ # search(q: '*:*', type: 'tweet')
202
201
  #
203
202
  # See https://www.elastic.co/guide/en/elasticsearch/reference/current/search-search.html
204
203
  # See https://www.elastic.co/guide/en/elasticsearch/reference/current/search-uri-request.html
@@ -208,7 +207,7 @@ module Elastomer
208
207
  def search( query, params = nil )
209
208
  query, params = extract_params(query) if params.nil?
210
209
 
211
- response = client.get "/{index}{/type}/_search", update_params(params, :body => query, :action => "docs.search")
210
+ response = client.get "/{index}{/type}/_search", update_params(params, body: query, action: "docs.search", rest_api: "search")
212
211
  response.body
213
212
  end
214
213
 
@@ -226,7 +225,7 @@ module Elastomer
226
225
  #
227
226
  # Returns the response body as a hash
228
227
  def search_shards( params = {} )
229
- response = client.get "/{index}{/type}/_search_shards", update_params(params, :action => "docs.search_shards")
228
+ response = client.get "/{index}{/type}/_search_shards", update_params(params, action: "docs.search_shards", rest_api: "search_shards")
230
229
  response.body
231
230
  end
232
231
 
@@ -242,10 +241,10 @@ module Elastomer
242
241
  # Examples
243
242
  #
244
243
  # # request body query
245
- # count({:match_all => {}}, :type => 'tweet')
244
+ # count({match_all: {}}, type: 'tweet')
246
245
  #
247
246
  # # same thing but using the URI request method
248
- # count(:q => '*:*', :type => 'tweet')
247
+ # count(q: '*:*', type: 'tweet')
249
248
  #
250
249
  # See https://www.elastic.co/guide/en/elasticsearch/reference/current/search-count.html
251
250
  #
@@ -253,7 +252,7 @@ module Elastomer
253
252
  def count( query, params = nil )
254
253
  query, params = extract_params(query) if params.nil?
255
254
 
256
- response = client.get "/{index}{/type}/_count", update_params(params, :body => query, :action => "docs.count")
255
+ response = client.get "/{index}{/type}/_count", update_params(params, body: query, action: "docs.count", rest_api: "count")
257
256
  response.body
258
257
  end
259
258
 
@@ -304,13 +303,13 @@ module Elastomer
304
303
  #
305
304
  # Examples
306
305
  #
307
- # index.percolator(1).create :query => { :match => { :author => "pea53" } }
308
- # docs.percolate :doc => { :author => "pea53" }
309
- # docs.percolate nil, :id => 3
306
+ # index.percolator(1).create query: { match: { author: "pea53" } }
307
+ # docs.percolate doc: { author: "pea53" }
308
+ # docs.percolate nil, id: 3
310
309
  #
311
310
  # Returns the response body as a Hash
312
311
  def percolate(body, params = {})
313
- response = client.get "/{index}/{type}{/id}/_percolate", update_params(params, :body => body, :action => "percolator.percolate")
312
+ response = client.get "/{index}/{type}{/id}/_percolate", update_params(params, body: body, action: "percolator.percolate", rest_api: "percolate")
314
313
  response.body
315
314
  end
316
315
 
@@ -320,13 +319,13 @@ module Elastomer
320
319
  #
321
320
  # Examples
322
321
  #
323
- # index.register_percolator_query 1, :query => { :match => { :author => "pea53" } }
324
- # docs.percolate_count :doc => { :author => "pea53" }
325
- # docs.percolate_count nil, :id => 3
322
+ # index.register_percolator_query 1, query: { match: { author: "pea53" } }
323
+ # docs.percolate_count doc: { author: "pea53" }
324
+ # docs.percolate_count nil, id: 3
326
325
  #
327
326
  # Returns the count
328
327
  def percolate_count(body, params = {})
329
- response = client.get "/{index}/{type}{/id}/_percolate/count", update_params(params, :body => body, :action => "percolator.percolate_count")
328
+ response = client.get "/{index}/{type}{/id}/_percolate/count", update_params(params, body: body, action: "percolator.percolate_count", rest_api: "count_percolate")
330
329
  response.body["total"]
331
330
  end
332
331
 
@@ -341,7 +340,7 @@ module Elastomer
341
340
  #
342
341
  # Returns the response body as a hash
343
342
  def termvector( params = {} )
344
- response = client.get "/{index}/{type}/{id}/_termvector", update_params(params, :action => "docs.termvector")
343
+ response = client.get "/{index}/{type}/{id}/_termvectors", update_params(params, action: "docs.termvector", rest_api: "termvectors")
345
344
  response.body
346
345
  end
347
346
  alias_method :termvectors, :termvector
@@ -359,7 +358,7 @@ module Elastomer
359
358
  #
360
359
  # Returns the response body as a hash
361
360
  def multi_termvectors( body, params = {} )
362
- response = client.get "{/index}{/type}/_mtermvectors", update_params(params, :body => body, :action => "docs.multi_termvectors")
361
+ response = client.get "{/index}{/type}/_mtermvectors", update_params(params, body: body, action: "docs.multi_termvectors", rest_api: "mtermvectors")
363
362
  response.body
364
363
  end
365
364
  alias_method :multi_term_vectors, :multi_termvectors
@@ -378,9 +377,9 @@ Percolate
378
377
  #
379
378
  # Examples
380
379
  #
381
- # explain({:query => {:term => {"message" => "search"}}}, :id => 1)
380
+ # explain({query: {term: {"message" => "search"}}}, id: 1)
382
381
  #
383
- # explain(:q => "message:search", :id => 1)
382
+ # explain(q: "message:search", id: 1)
384
383
  #
385
384
  # See https://www.elastic.co/guide/en/elasticsearch/reference/current/search-explain.html
386
385
  #
@@ -388,7 +387,7 @@ Percolate
388
387
  def explain( query, params = nil )
389
388
  query, params = extract_params(query) if params.nil?
390
389
 
391
- response = client.get "/{index}/{type}/{id}/_explain", update_params(params, :body => query, :action => "docs.explain")
390
+ response = client.get "/{index}/{type}/{id}/_explain", update_params(params, body: query, action: "docs.explain", rest_api: "explain")
392
391
  response.body
393
392
  end
394
393
 
@@ -402,10 +401,10 @@ Percolate
402
401
  # Examples
403
402
  #
404
403
  # # request body query
405
- # validate({:query => {:query_string => {:query => "*:*"}}}, :explain => true)
404
+ # validate({query: {query_string: {query: "*:*"}}}, explain: true)
406
405
  #
407
406
  # # same thing but using the URI query parameter
408
- # validate(:q => "post_date:foo", :explain => true)
407
+ # validate(q: "post_date:foo", explain: true)
409
408
  #
410
409
  # See https://www.elastic.co/guide/en/elasticsearch/reference/current/search-validate.html
411
410
  #
@@ -413,7 +412,7 @@ Percolate
413
412
  def validate( query, params = nil )
414
413
  query, params = extract_params(query) if params.nil?
415
414
 
416
- response = client.get "/{index}{/type}/_validate/query", update_params(params, :body => query, :action => "docs.validate")
415
+ response = client.get "/{index}{/type}/_validate/query", update_params(params, body: query, action: "docs.validate", rest_api: "indices.validate_query")
417
416
  response.body
418
417
  end
419
418
 
@@ -441,7 +440,7 @@ Percolate
441
440
  def bulk( params = {}, &block )
442
441
  raise "a block is required" if block.nil?
443
442
 
444
- params = {:index => self.name, :type => self.type}.merge params
443
+ params = {index: self.name, type: self.type}.merge params
445
444
  client.bulk params, &block
446
445
  end
447
446
 
@@ -467,7 +466,7 @@ Percolate
467
466
  #
468
467
  # Returns a new Scroller instance
469
468
  def scroll( query, opts = {} )
470
- opts = {:index => name, :type => type}.merge opts
469
+ opts = {index: name, type: type}.merge opts
471
470
  client.scroll query, opts
472
471
  end
473
472
 
@@ -494,7 +493,7 @@ Percolate
494
493
  #
495
494
  # Returns a new Scroller instance
496
495
  def scan( query, opts = {} )
497
- opts = {:index => name, :type => type}.merge opts
496
+ opts = {index: name, type: type}.merge opts
498
497
  client.scan query, opts
499
498
  end
500
499
 
@@ -514,8 +513,8 @@ Percolate
514
513
  # Examples
515
514
  #
516
515
  # docs.multi_search do |m|
517
- # m.search({:query => {:match_all => {}}, :size => 0)
518
- # m.search({:query => {:field => {"foo" => "bar"}}})
516
+ # m.search({query: {match_all: {}}, size: 0)
517
+ # m.search({query: {field: {"foo" => "bar"}}})
519
518
  # ...
520
519
  # end
521
520
  #
@@ -525,7 +524,7 @@ Percolate
525
524
  def multi_search( params = {}, &block )
526
525
  raise "a block is required" if block.nil?
527
526
 
528
- params = {:index => self.name, :type => self.type}.merge params
527
+ params = {index: self.name, type: self.type}.merge params
529
528
  client.multi_search params, &block
530
529
  end
531
530
 
@@ -543,8 +542,8 @@ Percolate
543
542
  #
544
543
  # # block form
545
544
  # multi_percolate do |m|
546
- # m.percolate(:author => "pea53")
547
- # m.count(:author => "grantr")
545
+ # m.percolate(author: "pea53")
546
+ # m.count(author: "grantr")
548
547
  # ...
549
548
  # end
550
549
  #
@@ -554,6 +553,11 @@ Percolate
554
553
  client.multi_percolate(params, &block)
555
554
  end
556
555
 
556
+ SPECIAL_KEYS= %i[
557
+ index type id version version_type op_type routing parent timestamp ttl
558
+ consistency replication refresh wait_for_active_shards
559
+ ].inject({}) { |h,k| h[k] = "_#{k}"; h }.freeze
560
+
557
561
  # Internal: Given a `document` generate an options hash that will
558
562
  # override parameters based on the content of the document. The document
559
563
  # will be returned as the value of the :body key.
@@ -568,26 +572,13 @@ Percolate
568
572
  # Raises Elastomer::Client::IllegalArgument if an unsupported indexing
569
573
  # directive is used.
570
574
  def from_document( document )
571
- opts = {:body => document}
575
+ opts = {body: document}
572
576
 
573
577
  if document.is_a? Hash
574
- client.version_support.indexing_directives.each do |key, field|
578
+ SPECIAL_KEYS.each do |key, field|
575
579
  opts[key] = document.delete field if document.key? field
576
580
  opts[key] = document.delete field.to_sym if document.key? field.to_sym
577
581
  end
578
-
579
- # COMPATIBILITY
580
- # Fail fast if a consumer is attempting to use an indexing parameter
581
- # for a different version of Elasticsearch. Elasticsearch 5+ is strict
582
- # about parameter names so we need to either ignore these (in which
583
- # case they would be indexed with the document) or fail-fast.
584
- # Elasticsearch 2.X will happily ignore unknown parameters, but we
585
- # felt it was best to consistently fail fast.
586
- client.version_support.unsupported_indexing_directives.each do |key, field|
587
- if document.key?(field) || document.key?(field.to_sym)
588
- raise IllegalArgument, "Elasticsearch #{client.version} does not support the '#{key}' indexing parameter"
589
- end
590
- end
591
582
  end
592
583
 
593
584
  opts
@@ -609,7 +600,7 @@ Percolate
609
600
 
610
601
  # Internal: Returns a Hash containing default parameters.
611
602
  def defaults
612
- { :index => name, :type => type }
603
+ { index: name, type: type }
613
604
  end
614
605
 
615
606
  # Internal: Allow params to be passed as the first argument to