logstash-filter-rest 0.2.1 → 0.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2af5930ffb298925b7a340afd929ac9c85559f4f
4
- data.tar.gz: 57e160ff830adbc6b63d22968e4f20047b1779a3
3
+ metadata.gz: 4fac99e78c6f1c75edbbf92e74fede3862dca591
4
+ data.tar.gz: d7ccae6caf784862a5f998ff48116cf96b44030a
5
5
  SHA512:
6
- metadata.gz: bedafb7870ae6c5d0fc22c81f4fcedd5b1384d6524afd8394da949502f6af8ce9d551615fa73751e112db6e44acfb51b90854874ac4199f0cc3cf53e55a7951a
7
- data.tar.gz: cb947ea8b5cbd4f666afcb297f2e45cc2f8bf5655db1f16b66de7a7a649eefc01332fb6e3156076b92ada1986d9e469027c5a8552f1880ab0a69be55f86efc1e
6
+ metadata.gz: b49a566196685abb817a20f4438f9fedcf14a33bb8c82a9a766ed0bdb91e7cbeeb9f18746e4eba277fbce034ad798c32361bf6e54e7bfd1c4ccfebb2eb891ad7
7
+ data.tar.gz: 15d9d11fb62f6bda9b4d67e70e753eb5b0e2a447f054213bcc8e9831a06b5358ffc635ea57e5988cc05fd8760bebcecace6bba7a5c9412d773a83aaed666d20f
data/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ ## 0.5.0
2
+ - Relax constraint on logstash-core-plugin-api to >= 1.60 <= 2.99
3
+ - Require devutils >= 0 to make `bundler` update the package
4
+ - Use Event API for LS-5
5
+ - Implicit `sprintf`, deprecating the setting
6
+ - `target` is now required, dropping support to write into top-level in favor of only using new Event API
7
+ - this follows other logstash-plugins like `logstash-filter-json`
8
+ - if the response is empty, add the restfailure tags
9
+ - Some logging moved before code
10
+ - Testcases adapted to new behavior with error check
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Logstash REST Filter [![Build Status](https://travis-ci.org/gandalfb/logstash-filter-rest.svg?branch=http-keep-alive)](https://travis-ci.org/gandalfb/logstash-filter-rest)
1
+ # Logstash REST Filter [![Build Status](https://travis-ci.org/gandalfb/logstash-filter-rest.svg?branch=version%2Flogstash-5)](https://travis-ci.org/gandalfb/logstash-filter-rest)
2
2
 
3
3
  This is a filter plugin for [Logstash](https://github.com/elasticsearch/logstash).
4
4
 
@@ -30,8 +30,8 @@ Add the following inside the filter section of your logstash configuration:
30
30
  filter {
31
31
  rest {
32
32
  request => {
33
- url => "http://example.com" # string (required, with field reference: "http://example.com?id=%{id}" or params, if defined)
34
- method => "post" # string (optional, default = "get")
33
+ url => "http://example.com" # string (required, with field reference: "http://example.com?id=%{id}" or params, if defined)
34
+ method => "post" # string (optional, default = "get")
35
35
  headers => { # hash (optional)
36
36
  "key1" => "value1"
37
37
  "key2" => "value2"
@@ -40,16 +40,15 @@ filter {
40
40
  user => "AzureDiamond"
41
41
  password => "hunter2"
42
42
  }
43
- params => { # hash (optional, available for method => "get" and "post"; if post it will be transformed into body hash and posted as json)
43
+ params => { # hash (optional, available for method => "get" and "post"; if post it will be transformed into body hash and posted as json)
44
44
  "key1" => "value1"
45
45
  "key2" => "value2"
46
- "key3" => "%{somefield}" # Please set sprintf to true if you want to use field references
46
+ "key3" => "%{somefield}" # sprintf is used implicitly
47
47
  }
48
48
  }
49
- json => true # boolean (optional, default = false)
50
- sprintf => true # boolean (optional, default = false, set this to true if you want to use field references in url, header or params)
51
- target => "my_key" # string (optional, default = "rest_response")
52
- fallback => { # hash describing a default in case of error
49
+ json => true # boolean (optional, default = true)
50
+ target => "my_key" # string (mandatory, no default)
51
+ fallback => { # hash describing a default in case of error
53
52
  "key1" => "value1"
54
53
  "key2" => "value2"
55
54
  }
@@ -57,6 +56,31 @@ filter {
57
56
  }
58
57
  ```
59
58
 
59
+ Print plugin version:
60
+
61
+ ``` bash
62
+ bin/logstash-plugin list --verbose | grep rest
63
+ ```
64
+
65
+ Examples for running logstash from `cli`:
66
+
67
+ ``` bash
68
+ bin/logstash --debug -e 'input { stdin{} } filter { rest { request => { url => "https://jsonplaceholder.typicode.com/posts" method => "post" params => { "userId" => "%{message}" } headers => { "Content-Type" => "application/json" } } target => 'rest' } } output {stdout { codec => rubydebug }}'
69
+ ```
70
+
71
+ ``` bash
72
+ bin/logstash --debug -e 'input { stdin{} } filter { rest { request => { url => "https://jsonplaceholder.typicode.com/posts" method => "post" body => { "userId" => "%{message}" } headers => { "Content-Type" => "application/json" } } target => 'rest' } } output {stdout { codec => rubydebug }}'
73
+ ```
74
+
75
+ ``` bash
76
+ bin/logstash --debug -e 'input { stdin{} } filter { rest { request => { url => "http://jsonplaceholder.typicode.com/users/%{message}" } target => 'rest' } } output {stdout { codec => rubydebug }}'
77
+ ```
78
+
79
+ ``` bash
80
+ bin/logstash --debug -e 'input { stdin{} } filter { rest { request => { url => "https://jsonplaceholder.typicode.com/posts" method => "get" params => { "userId" => "%{message}" } headers => { "Content-Type" => "application/json" } } target => 'rest' } } output {stdout { codec => rubydebug }}'
81
+ ```
82
+
83
+
60
84
  ## Contributing
61
85
 
62
86
  All contributions are welcome: ideas, patches, documentation, bug reports, complaints, and even something you drew up on a napkin.
@@ -4,6 +4,36 @@ require 'logstash/namespace'
4
4
  require 'logstash/plugin_mixins/http_client'
5
5
  require 'logstash/json'
6
6
 
7
+ # Monkey Patch hsh with a recursive compact and deep freeze
8
+ class Hash
9
+ def compact
10
+ delete_if { |_k, v| v.respond_to?(:each) ? v.compact.empty? : v.nil? }
11
+ end
12
+
13
+ def deep_freeze
14
+ each { |_k, v| v.deep_freeze if v.respond_to? :deep_freeze }
15
+ freeze
16
+ end
17
+ end
18
+
19
+ # Monkey Patch string to parse to hsh
20
+ class String
21
+ def to_object(symbolize = true)
22
+ LogStash::Json.load(
23
+ gsub(/:([a-zA-z]+)/, '"\\1"').gsub('=>', ': '),
24
+ :symbolize_keys => symbolize
25
+ )
26
+ end
27
+ end
28
+
29
+ # Monkey Patch Array with deep freeze
30
+ class Array
31
+ def deep_freeze
32
+ each { |j| j.deep_freeze if j.respond_to? :deep_freeze }
33
+ freeze
34
+ end
35
+ end
36
+
7
37
  # Logstash REST Filter
8
38
  # This filter calls a defined URL and saves the answer into a specified field.
9
39
  #
@@ -22,7 +52,7 @@ class LogStash::Filters::Rest < LogStash::Filters::Base
22
52
  # request => {
23
53
  # url => "http://example.com" # string (required, with field reference: "http://example.com?id=%{id}" or params, if defined)
24
54
  # method => "post" # string (optional, default = "get")
25
- # headers => { # hash (optional)
55
+ # headers => { # hash (optional)
26
56
  # "key1" => "value1"
27
57
  # "key2" => "value2"
28
58
  # }
@@ -33,9 +63,10 @@ class LogStash::Filters::Rest < LogStash::Filters::Base
33
63
  # params => { # hash (optional, available for method => "get" and "post"; if post it will be transformed into body hash and posted as json)
34
64
  # "key1" => "value1"
35
65
  # "key2" => "value2"
36
- # "key3" => "%{somefield}" # Please set sprintf to true if you want to use field references
66
+ # "key3" => "%{somefield}" # Field references are found implicitly on startup
37
67
  # }
38
68
  # }
69
+ # target => "doc"
39
70
  # }
40
71
  # }
41
72
  #
@@ -48,7 +79,6 @@ class LogStash::Filters::Rest < LogStash::Filters::Base
48
79
  # [source,ruby]
49
80
  # filter {
50
81
  # rest {
51
- # request => { .. }
52
82
  # json => true
53
83
  # }
54
84
  # }
@@ -64,22 +94,24 @@ class LogStash::Filters::Rest < LogStash::Filters::Base
64
94
  # sprintf => true
65
95
  # }
66
96
  # }
67
- config :sprintf, :validate => :boolean, :default => false
97
+ config :sprintf, :validate => :boolean, :default => false, :deprecated => true
68
98
 
69
- # Defines the field, where the parsed response is written to
70
- # if set to '' it will be written to event root
99
+ # Define the target field for placing the response data. This setting is
100
+ # required and may not be omitted. It is not possible to place the response
101
+ # into the event top-level.
71
102
  #
72
103
  # For example, if you want the data to be put in the `doc` field:
73
104
  # [source,ruby]
74
105
  # filter {
75
106
  # rest {
76
- # request => { .. }
77
107
  # target => "doc"
78
108
  # }
79
109
  # }
80
110
  #
111
+ # Rest response will be expanded into a data structure in the `target` field.
112
+ #
81
113
  # NOTE: if the `target` field already exists, it will be overwritten!
82
- config :target, :validate => :string, :default => 'rest'
114
+ config :target, :validate => :string, :required => true
83
115
 
84
116
  # If set, any error like json parsing or invalid http response
85
117
  # will result in this hash to be added to target instead of error tags
@@ -88,7 +120,6 @@ class LogStash::Filters::Rest < LogStash::Filters::Base
88
120
  # [source,ruby]
89
121
  # filter {
90
122
  # rest {
91
- # request => { .. }
92
123
  # fallback => {
93
124
  # 'key1' => 'value1'
94
125
  # 'key2' => 'value2'
@@ -106,11 +137,25 @@ class LogStash::Filters::Rest < LogStash::Filters::Base
106
137
  public
107
138
 
108
139
  def register
109
- @request = normalize_request(@request).freeze
140
+ @request = normalize_request(@request)
141
+ @sprintf_fields = find_sprintf(
142
+ Marshal.load(Marshal.dump(@request))
143
+ ).deep_freeze
144
+ @target = normalize_target(@target)
110
145
  end # def register
111
146
 
112
147
  private
113
148
 
149
+ def normalize_target(target)
150
+ # make sure @target is in the format [field name] if defined,
151
+ # i.e. not empty and surrounded by brakets
152
+ raise LogStash::ConfigurationError, 'target config string is empty, please set a valid field name' if target.empty?
153
+ target = "[#{target}]" if target && target !~ /^\[[^\[\]]+\]$/
154
+ target
155
+ end
156
+
157
+ private
158
+
114
159
  def normalize_request(url_or_spec)
115
160
  if url_or_spec.is_a?(String)
116
161
  res = [:get, url_or_spec]
@@ -123,12 +168,12 @@ class LogStash::Filters::Rest < LogStash::Filters::Base
123
168
  url = spec.delete(:url)
124
169
 
125
170
  # if it is a post and json, it is used as body string, not params
126
- spec[:body] = spec.delete(:params) if method == :post
171
+ spec[:body] = spec.delete(:params) if method == :post && spec[:params]
127
172
 
128
173
  # We need these strings to be keywords!
129
174
  spec[:auth] = { user: spec[:auth]['user'], pass: spec[:auth]['password'] } if spec[:auth]
130
175
 
131
- res = [method, url, spec]
176
+ res = [method.freeze, url, spec]
132
177
  else
133
178
  raise LogStash::ConfigurationError, "Invalid URL or request spec: '#{url_or_spec}', expected a String or Hash!"
134
179
  end
@@ -155,10 +200,27 @@ class LogStash::Filters::Rest < LogStash::Filters::Base
155
200
 
156
201
  private
157
202
 
158
- def request_http(request)
159
- @logger.debug? && @logger.debug('Fetching URL', :request => request)
203
+ def find_sprintf(config)
204
+ field_matcher = /%\{[^}]+\}/
205
+ if config.is_a?(Hash)
206
+ config.keep_if do |_k, v|
207
+ find_sprintf(v)
208
+ end.compact
209
+ elsif config.is_a?(Array)
210
+ config.keep_if do |v|
211
+ find_sprintf(v)
212
+ end.compact
213
+ elsif config.is_a?(String) && config =~ field_matcher
214
+ config
215
+ end
216
+ end
160
217
 
218
+ private
219
+
220
+ def request_http(request)
161
221
  request[2][:body] = LogStash::Json.dump(request[2][:body]) if request[2].key?(:body)
222
+ @logger.debug? && @logger.debug('Fetching request',
223
+ :request => request)
162
224
 
163
225
  method, url, *request_opts = request
164
226
  response = client.http(method, url, *request_opts)
@@ -171,65 +233,64 @@ class LogStash::Filters::Rest < LogStash::Filters::Base
171
233
  if @json
172
234
  begin
173
235
  parsed = LogStash::Json.load(response)
174
- event = add_to_event(parsed, event)
236
+ if parsed.empty?
237
+ @logger.warn('rest response empty',
238
+ :response => response, :event => event)
239
+ @tag_on_rest_failure.each { |tag| event.tag(tag) }
240
+ else
241
+ event.set(@target, parsed)
242
+ end
175
243
  rescue
176
244
  if @fallback.empty?
245
+ @logger.warn('JSON parsing error',
246
+ :response => response, :event => event)
177
247
  @tag_on_json_failure.each { |tag| event.tag(tag) }
178
- @logger.warn('JSON parsing error', :response => response, :event => event)
179
248
  else
180
- event = add_to_event(@fallback, event)
249
+ event.set(@target, @fallback)
181
250
  end
182
251
  end
183
252
  else
184
253
  event.set(@target, response.strip)
185
254
  end
186
- event
187
255
  end
188
256
 
189
257
  public
190
258
 
191
259
  def filter(event)
192
260
  return unless filter?(event)
193
- request = @request.dup
194
- request[2][:params] = sprint(@sprintf, @request[2][:params], event) if request[2].key?(:params)
195
- request[2][:body] = sprint(@sprintf, @request[2][:body], event) if request[2].key?(:body)
196
- request[1] = sprint(@sprintf, @request[1], event)
261
+ @logger.debug? && @logger.debug('Parsing event fields',
262
+ :sprintf_fields => @sprintf_fields)
263
+ parsed_request_fields = event.sprintf(@sprintf_fields).to_object
264
+ parsed_request_fields.each do |v|
265
+ case v
266
+ when Hash
267
+ @request[2].merge!(v)
268
+ when String
269
+ @request[1] = v
270
+ end
271
+ end
272
+ @logger.debug? && @logger.debug('Parsed request',
273
+ :request => @request)
197
274
 
198
- code, body = request_http(request)
275
+ code, body = request_http(@request)
199
276
  if code.between?(200, 299)
200
- event = process_response(body, event)
201
- @logger.debug? && @logger.debug('Sucess received', :code => code, :body => body)
277
+ @logger.debug? && @logger.debug('Sucess received',
278
+ :code => code, :body => body)
279
+ process_response(body, event)
202
280
  else
203
- @logger.debug? && @logger.debug('Http error received', :code => code, :body => body)
281
+ @logger.debug? && @logger.debug('Http error received',
282
+ :code => code, :body => body)
204
283
  if @fallback.empty?
284
+ @logger.error('Error in Rest filter',
285
+ :request => @request, :json => @json,
286
+ :code => code, :body => body)
205
287
  @tag_on_rest_failure.each { |tag| event.tag(tag) }
206
- @logger.error('Error in Rest filter', :request => request, :json => @json, :code => code, :body => body)
207
288
  else
208
- event = add_to_event(@fallback, event)
209
- @logger.debug? && @logger.debug('Setting fallback', :fallback => @fallback)
289
+ @logger.debug? && @logger.debug('Setting fallback',
290
+ :fallback => @fallback)
291
+ event.set(@target, @fallback)
210
292
  end
211
293
  end
212
294
  filter_matched(event)
213
295
  end # def filter
214
-
215
- private
216
-
217
- def sprint(sprintf, hash, event)
218
- return hash unless sprintf
219
- return event.sprintf(hash) unless hash.is_a?(Hash)
220
- result = {}
221
- hash.each { |k, v| result[k] = event.sprintf(v) }
222
- result
223
- end
224
-
225
- private
226
-
227
- def add_to_event(to_add, event)
228
- if @target.empty?
229
- to_add.each { |k, v| event[k] = v }
230
- else
231
- event[@target] = to_add
232
- end
233
- event
234
- end
235
296
  end # class LogStash::Filters::Rest
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'logstash-filter-rest'
3
- s.version = '0.2.1'
3
+ s.version = '0.5.0'
4
4
  s.licenses = ['Apache License (2.0)']
5
5
  s.summary = 'This filter requests data from a RESTful Web Service.'
6
6
  s.description = 'This gem is a logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/plugin install gemname. This gem is not a stand-alone program'
@@ -10,7 +10,15 @@ Gem::Specification.new do |s|
10
10
  s.require_paths = ['lib']
11
11
 
12
12
  # Files
13
- s.files = Dir['lib/**/*','spec/**/*','vendor/**/*','*.gemspec','*.md','CONTRIBUTORS','Gemfile','LICENSE','NOTICE.TXT']
13
+ s.files = Dir['lib/**/*',
14
+ 'spec/**/*',
15
+ 'vendor/**/*',
16
+ '*.gemspec',
17
+ '*.md',
18
+ 'CONTRIBUTORS',
19
+ 'Gemfile',
20
+ 'LICENSE',
21
+ 'NOTICE.TXT']
14
22
  # Tests
15
23
  s.test_files = s.files.grep(%r{^(test|spec|features)/})
16
24
 
@@ -18,10 +26,8 @@ Gem::Specification.new do |s|
18
26
  s.metadata = { 'logstash_plugin' => 'true', 'logstash_group' => 'filter' }
19
27
 
20
28
  # Gem dependencies
21
- s.add_runtime_dependency 'logstash-core', '>= 1.6.0', '< 3.0.0'
22
- s.add_runtime_dependency 'logstash-codec-json', '>= 1.6.0', '< 3.0.0'
29
+ s.add_runtime_dependency 'logstash-core-plugin-api', '>= 1.60', '<= 2.99'
23
30
  s.add_runtime_dependency 'logstash-mixin-http_client', '>= 2.2.4', '< 5.0.0'
24
31
 
25
- s.add_development_dependency 'logstash-devutils', '~> 0'
26
- s.add_development_dependency 'pry', '~> 0'
32
+ s.add_development_dependency 'logstash-devutils', '>= 0', '< 2.0.0'
27
33
  end
@@ -1,33 +1,34 @@
1
- require 'spec_helper'
1
+ require 'logstash/devutils/rspec/spec_helper'
2
2
  require 'logstash/filters/rest'
3
3
 
4
4
  describe LogStash::Filters::Rest do
5
- describe "Set to Rest Filter Get without params" do
5
+ describe 'Set to Rest Filter Get without params' do
6
6
  let(:config) do <<-CONFIG
7
7
  filter {
8
8
  rest {
9
9
  request => {
10
- url => "http://jsonplaceholder.typicode.com/users/10"
10
+ url => 'http://jsonplaceholder.typicode.com/users/10'
11
11
  }
12
12
  json => true
13
+ target => 'rest'
13
14
  }
14
15
  }
15
16
  CONFIG
16
17
  end
17
18
 
18
- sample("message" => "some text") do
19
+ sample('message' => 'some text') do
19
20
  expect(subject).to include('rest')
20
- expect(subject['rest']).to include("id")
21
- expect(subject['rest']['id']).to eq(10)
22
- expect(subject['rest']).to_not include("fallback")
21
+ expect(subject.get('rest')).to include('id')
22
+ expect(subject.get('[rest][id]')).to eq(10)
23
+ expect(subject.get('rest')).to_not include('fallback')
23
24
  end
24
25
  end
25
- describe "Set to Rest Filter Get without params custom target" do
26
+ describe 'Set to Rest Filter Get without params custom target' do
26
27
  let(:config) do <<-CONFIG
27
28
  filter {
28
29
  rest {
29
30
  request => {
30
- url => "http://jsonplaceholder.typicode.com/users/10"
31
+ url => 'http://jsonplaceholder.typicode.com/users/10'
31
32
  }
32
33
  json => true
33
34
  target => 'testing'
@@ -36,14 +37,14 @@ describe LogStash::Filters::Rest do
36
37
  CONFIG
37
38
  end
38
39
 
39
- sample("message" => "some text") do
40
+ sample('message' => 'some text') do
40
41
  expect(subject).to include('testing')
41
- expect(subject['testing']).to include("id")
42
- expect(subject['testing']['id']).to eq(10)
43
- expect(subject['testing']).to_not include("fallback")
42
+ expect(subject.get('testing')).to include('id')
43
+ expect(subject.get('[testing][id]')).to eq(10)
44
+ expect(subject.get('testing')).to_not include('fallback')
44
45
  end
45
46
  end
46
- describe "Set to Rest Filter Get without params and sprintf" do
47
+ describe 'Set to Rest Filter Get without params and sprintf' do
47
48
  let(:config) do <<-CONFIG
48
49
  filter {
49
50
  rest {
@@ -52,226 +53,279 @@ describe LogStash::Filters::Rest do
52
53
  }
53
54
  json => true
54
55
  sprintf => true
56
+ target => 'rest'
55
57
  }
56
58
  }
57
59
  CONFIG
58
60
  end
59
61
 
60
- sample("message" => "10") do
62
+ sample('message' => '10') do
61
63
  expect(subject).to include('rest')
62
- expect(subject['rest']).to include("id")
63
- expect(subject['rest']['id']).to eq(10)
64
- expect(subject['rest']).to_not include("fallback")
64
+ expect(subject.get('rest')).to include('id')
65
+ expect(subject.get('[rest][id]')).to eq(10)
66
+ expect(subject.get('rest')).to_not include('fallback')
65
67
  end
66
- sample("message" => "9") do
68
+ sample('message' => '9') do
67
69
  expect(subject).to include('rest')
68
- expect(subject['rest']).to include("id")
69
- expect(subject['rest']['id']).to eq(9)
70
- expect(subject['rest']).to_not include("fallback")
70
+ expect(subject.get('rest')).to include('id')
71
+ expect(subject.get('[rest][id]')).to eq(9)
72
+ expect(subject.get('rest')).to_not include('fallback')
71
73
  end
72
74
  end
73
- describe "Set to Rest Filter Get without params http error" do
75
+ describe 'Set to Rest Filter Get without params http error' do
74
76
  let(:config) do <<-CONFIG
75
77
  filter {
76
78
  rest {
77
79
  request => {
78
- url => "http://httpstat.us/404"
80
+ url => 'http://httpstat.us/404'
79
81
  }
80
82
  json => true
83
+ target => 'rest'
81
84
  }
82
85
  }
83
86
  CONFIG
84
87
  end
85
88
 
86
- sample("message" => "some text") do
89
+ sample('message' => 'some text') do
87
90
  expect(subject).to_not include('rest')
88
- expect(subject['tags']).to include('_restfailure')
91
+ expect(subject.get('tags')).to include('_restfailure')
89
92
  end
90
93
  end
91
- describe "Set to Rest Filter Get with params" do
94
+ describe 'Set to Rest Filter Get with params' do
92
95
  let(:config) do <<-CONFIG
93
96
  filter {
94
97
  rest {
95
98
  request => {
96
- url => "https://jsonplaceholder.typicode.com/posts"
99
+ url => 'https://jsonplaceholder.typicode.com/posts'
97
100
  params => {
98
101
  userId => 10
99
102
  }
100
103
  headers => {
101
- "Content-Type" => "application/json"
104
+ 'Content-Type' => 'application/json'
102
105
  }
103
106
  }
104
107
  json => true
108
+ target => 'rest'
105
109
  }
106
110
  }
107
111
  CONFIG
108
112
  end
109
113
 
110
- sample("message" => "some text") do
114
+ sample('message' => 'some text') do
111
115
  expect(subject).to include('rest')
112
- expect(subject['rest'][0]).to include("userId")
113
- expect(subject['rest'][0]['userId']).to eq(10)
114
- expect(subject['rest']).to_not include("fallback")
116
+ expect(subject.get('[rest][0]')).to include('userId')
117
+ expect(subject.get('[rest][0][userId]')).to eq(10)
118
+ expect(subject.get('rest')).to_not include('fallback')
115
119
  end
116
120
  end
117
- describe "Set to Rest Filter Get with params sprintf" do
121
+ describe 'empty response' do
118
122
  let(:config) do <<-CONFIG
119
123
  filter {
120
124
  rest {
121
125
  request => {
122
- url => "https://jsonplaceholder.typicode.com/posts"
126
+ url => 'https://jsonplaceholder.typicode.com/posts'
127
+ params => {
128
+ userId => 0
129
+ }
130
+ headers => {
131
+ 'Content-Type' => 'application/json'
132
+ }
133
+ }
134
+ target => 'rest'
135
+ }
136
+ }
137
+ CONFIG
138
+ end
139
+
140
+ sample('message' => 'some text') do
141
+ expect(subject).to_not include('rest')
142
+ expect(subject.get('tags')).to include('_restfailure')
143
+ end
144
+ end
145
+ describe 'Set to Rest Filter Get with params sprintf' do
146
+ let(:config) do <<-CONFIG
147
+ filter {
148
+ rest {
149
+ request => {
150
+ url => 'https://jsonplaceholder.typicode.com/posts'
123
151
  params => {
124
152
  userId => "%{message}"
153
+ id => "%{message}"
125
154
  }
126
155
  headers => {
127
- "Content-Type" => "application/json"
156
+ 'Content-Type' => 'application/json'
128
157
  }
129
158
  }
130
159
  json => true
131
- sprintf => true
160
+ target => 'rest'
132
161
  }
133
162
  }
134
163
  CONFIG
135
164
  end
136
165
 
137
- sample("message" => "10") do
138
- expect(subject).to include('rest')
139
- expect(subject['rest'][0]).to include("userId")
140
- expect(subject['rest'][0]['userId']).to eq(10)
141
- expect(subject['rest']).to_not include("fallback")
142
- end
143
- sample("message" => "9") do
166
+ sample('message' => '1') do
144
167
  expect(subject).to include('rest')
145
- expect(subject['rest'][0]).to include("userId")
146
- expect(subject['rest'][0]['userId']).to eq(9)
147
- expect(subject['rest']).to_not include("fallback")
168
+ expect(subject.get('[rest][0]')).to include('userId')
169
+ expect(subject.get('[rest][0][userId]')).to eq(1)
170
+ expect(subject.get('[rest][0][id]')).to eq(1)
171
+ expect(subject.get('rest').length).to eq(1)
172
+ expect(subject.get('rest')).to_not include('fallback')
148
173
  end
149
174
  end
150
- describe "Set to Rest Filter Post with params" do
175
+ describe 'Set to Rest Filter Post with params' do
151
176
  let(:config) do <<-CONFIG
152
177
  filter {
153
178
  rest {
154
179
  request => {
155
- url => "https://jsonplaceholder.typicode.com/posts"
156
- method => "post"
180
+ url => 'https://jsonplaceholder.typicode.com/posts'
181
+ method => 'post'
157
182
  params => {
158
183
  title => 'foo'
159
184
  body => 'bar'
160
185
  userId => 42
161
186
  }
162
187
  headers => {
163
- "Content-Type" => "application/json"
188
+ 'Content-Type' => 'application/json'
164
189
  }
165
190
  }
166
191
  json => true
192
+ target => 'rest'
167
193
  }
168
194
  }
169
195
  CONFIG
170
196
  end
171
197
 
172
- sample("message" => "some text") do
198
+ sample('message' => 'some text') do
173
199
  expect(subject).to include('rest')
174
- expect(subject['rest']).to include("id")
175
- expect(subject['rest']['userId']).to eq(42)
176
- expect(subject['rest']).to_not include("fallback")
200
+ expect(subject.get('rest')).to include('id')
201
+ expect(subject.get('[rest][userId]')).to eq(42)
202
+ expect(subject.get('rest')).to_not include('fallback')
177
203
  end
178
204
  end
179
- describe "Set to Rest Filter Post with params sprintf" do
205
+ describe 'Set to Rest Filter Post with params sprintf' do
180
206
  let(:config) do <<-CONFIG
181
207
  filter {
182
208
  rest {
183
209
  request => {
184
- url => "https://jsonplaceholder.typicode.com/posts"
185
- method => "post"
210
+ url => 'https://jsonplaceholder.typicode.com/posts'
211
+ method => 'post'
186
212
  params => {
187
213
  title => 'foo'
188
214
  body => 'bar'
189
215
  userId => "%{message}"
190
216
  }
191
217
  headers => {
192
- "Content-Type" => "application/json"
218
+ 'Content-Type' => 'application/json'
193
219
  }
194
220
  }
195
221
  json => true
196
- sprintf => true
222
+ target => 'rest'
197
223
  }
198
224
  }
199
225
  CONFIG
200
226
  end
201
227
 
202
- sample("message" => "42") do
228
+ sample('message' => '42') do
203
229
  expect(subject).to include('rest')
204
- expect(subject['rest']).to include("id")
205
- expect(subject['rest']['userId']).to eq(42)
206
- expect(subject['rest']).to_not include("fallback")
230
+ expect(subject.get('rest')).to include('id')
231
+ expect(subject.get('[rest][userId]')).to eq(42)
232
+ expect(subject.get('rest')).to_not include('fallback')
207
233
  end
208
234
  end
209
- describe "Fallback" do
235
+ describe 'Set to Rest Filter Post with body sprintf' do
210
236
  let(:config) do <<-CONFIG
211
237
  filter {
212
238
  rest {
213
239
  request => {
214
- url => "http://jsonplaceholder.typicode.com/users/0"
240
+ url => 'https://jsonplaceholder.typicode.com/posts'
241
+ method => 'post'
242
+ body => {
243
+ title => 'foo'
244
+ body => 'bar'
245
+ userId => "%{message}"
246
+ }
247
+ headers => {
248
+ 'Content-Type' => 'application/json'
249
+ }
215
250
  }
216
251
  json => true
217
- fallback => {
218
- "fallback1" => true
219
- "fallback2" => true
220
- }
252
+ target => 'rest'
221
253
  }
222
254
  }
223
255
  CONFIG
224
256
  end
225
257
 
226
- sample("message" => "some text") do
258
+ sample('message' => '42') do
227
259
  expect(subject).to include('rest')
228
- expect(subject['rest']).to include("fallback1")
229
- expect(subject['rest']).to include("fallback2")
230
- expect(subject['rest']).to_not include("id")
260
+ expect(subject.get('rest')).to include('id')
261
+ expect(subject.get('[rest][userId]')).to eq(42)
262
+ expect(subject.get('rest')).to_not include('fallback')
231
263
  end
232
264
  end
233
- describe "Fallback empty target" do
265
+ describe 'fallback' do
234
266
  let(:config) do <<-CONFIG
235
267
  filter {
236
268
  rest {
237
269
  request => {
238
- url => "http://jsonplaceholder.typicode.com/users/0"
270
+ url => 'http://jsonplaceholder.typicode.com/users/0'
239
271
  }
240
272
  json => true
241
- target => ''
242
273
  fallback => {
243
- "fallback1" => true
244
- "fallback2" => true
274
+ 'fallback1' => true
275
+ 'fallback2' => true
245
276
  }
277
+ target => 'rest'
246
278
  }
247
279
  }
248
280
  CONFIG
249
281
  end
250
282
 
251
- sample("message" => "some text") do
252
- expect(subject).to_not include('rest')
253
- expect(subject).to include("fallback1")
254
- expect(subject).to include("fallback2")
255
- expect(subject).to_not include("id")
283
+ sample('message' => 'some text') do
284
+ expect(subject).to include('rest')
285
+ expect(subject.get('rest')).to include('fallback1')
286
+ expect(subject.get('rest')).to include('fallback2')
287
+ expect(subject.get('rest')).to_not include('id')
256
288
  end
257
289
  end
258
- describe "Empty target" do
290
+ describe 'empty target exception' do
259
291
  let(:config) do <<-CONFIG
260
292
  filter {
261
293
  rest {
262
294
  request => {
263
- url => "http://jsonplaceholder.typicode.com/users/1"
295
+ url => 'http://jsonplaceholder.typicode.com/users/0'
264
296
  }
265
297
  json => true
298
+ fallback => {
299
+ 'fallback1' => true
300
+ 'fallback2' => true
301
+ }
266
302
  target => ''
267
303
  }
268
304
  }
269
305
  CONFIG
270
306
  end
271
-
272
- sample("message" => "some text") do
273
- expect(subject).to include("id")
274
- expect(subject).to_not include("fallback")
307
+ sample('message' => 'some text') do
308
+ expect { subject }.to raise_error(LogStash::ConfigurationError)
309
+ end
310
+ end
311
+ describe 'missing target exception' do
312
+ let(:config) do <<-CONFIG
313
+ filter {
314
+ rest {
315
+ request => {
316
+ url => 'http://jsonplaceholder.typicode.com/users/0'
317
+ }
318
+ json => true
319
+ fallback => {
320
+ 'fallback1' => true
321
+ 'fallback2' => true
322
+ }
323
+ }
324
+ }
325
+ CONFIG
326
+ end
327
+ sample('message' => 'some text') do
328
+ expect { subject }.to raise_error(LogStash::ConfigurationError)
275
329
  end
276
330
  end
277
331
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-filter-rest
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lucas Henning
@@ -9,48 +9,28 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-08-21 00:00:00.000000000 Z
12
+ date: 2016-12-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 1.6.0
20
- - - "<"
21
- - !ruby/object:Gem::Version
22
- version: 3.0.0
23
- name: logstash-core
24
- prerelease: false
25
- type: :runtime
26
- version_requirements: !ruby/object:Gem::Requirement
27
- requirements:
28
- - - ">="
29
- - !ruby/object:Gem::Version
30
- version: 1.6.0
31
- - - "<"
32
- - !ruby/object:Gem::Version
33
- version: 3.0.0
34
- - !ruby/object:Gem::Dependency
35
- requirement: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - ">="
38
- - !ruby/object:Gem::Version
39
- version: 1.6.0
40
- - - "<"
19
+ version: '1.60'
20
+ - - "<="
41
21
  - !ruby/object:Gem::Version
42
- version: 3.0.0
43
- name: logstash-codec-json
22
+ version: '2.99'
23
+ name: logstash-core-plugin-api
44
24
  prerelease: false
45
25
  type: :runtime
46
26
  version_requirements: !ruby/object:Gem::Requirement
47
27
  requirements:
48
28
  - - ">="
49
29
  - !ruby/object:Gem::Version
50
- version: 1.6.0
51
- - - "<"
30
+ version: '1.60'
31
+ - - "<="
52
32
  - !ruby/object:Gem::Version
53
- version: 3.0.0
33
+ version: '2.99'
54
34
  - !ruby/object:Gem::Dependency
55
35
  requirement: !ruby/object:Gem::Requirement
56
36
  requirements:
@@ -74,37 +54,30 @@ dependencies:
74
54
  - !ruby/object:Gem::Dependency
75
55
  requirement: !ruby/object:Gem::Requirement
76
56
  requirements:
77
- - - "~>"
57
+ - - ">="
78
58
  - !ruby/object:Gem::Version
79
59
  version: '0'
60
+ - - "<"
61
+ - !ruby/object:Gem::Version
62
+ version: 2.0.0
80
63
  name: logstash-devutils
81
64
  prerelease: false
82
65
  type: :development
83
66
  version_requirements: !ruby/object:Gem::Requirement
84
67
  requirements:
85
- - - "~>"
86
- - !ruby/object:Gem::Version
87
- version: '0'
88
- - !ruby/object:Gem::Dependency
89
- requirement: !ruby/object:Gem::Requirement
90
- requirements:
91
- - - "~>"
68
+ - - ">="
92
69
  - !ruby/object:Gem::Version
93
70
  version: '0'
94
- name: pry
95
- prerelease: false
96
- type: :development
97
- version_requirements: !ruby/object:Gem::Requirement
98
- requirements:
99
- - - "~>"
71
+ - - "<"
100
72
  - !ruby/object:Gem::Version
101
- version: '0'
73
+ version: 2.0.0
102
74
  description: This gem is a logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/plugin install gemname. This gem is not a stand-alone program
103
75
  email: mail@hurb.de
104
76
  executables: []
105
77
  extensions: []
106
78
  extra_rdoc_files: []
107
79
  files:
80
+ - CHANGELOG.md
108
81
  - CONTRIBUTORS
109
82
  - Gemfile
110
83
  - LICENSE
@@ -112,7 +85,6 @@ files:
112
85
  - lib/logstash/filters/rest.rb
113
86
  - logstash-filter-rest.gemspec
114
87
  - spec/filters/rest_spec.rb
115
- - spec/spec_helper.rb
116
88
  homepage: https://github.com/lucashenning/logstash-filter-rest/
117
89
  licenses:
118
90
  - Apache License (2.0)
@@ -141,4 +113,3 @@ specification_version: 4
141
113
  summary: This filter requests data from a RESTful Web Service.
142
114
  test_files:
143
115
  - spec/filters/rest_spec.rb
144
- - spec/spec_helper.rb
data/spec/spec_helper.rb DELETED
@@ -1,2 +0,0 @@
1
- require 'logstash/devutils/rspec/spec_helper'
2
- require 'pry'