logstash-input-twitter 3.0.2 → 3.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 03868427c8be1712775c28da42157a05ac74260b
4
- data.tar.gz: 62ac75929f623f07047ca067e7b4b70f7f817f17
3
+ metadata.gz: ed7c67082ab095da36c316dbff0a2fb9ada5b5d0
4
+ data.tar.gz: 7be4fb0d61eeddd69c8976cdbc66408b8f32d932
5
5
  SHA512:
6
- metadata.gz: a0ad512d28dff9ff61c6ecb5a47d5990105e8e321980f6026d5466f82703781ca63e61823ef90bc0340c2cf6b60168679fdc9f40131922a64292686893e9628a
7
- data.tar.gz: a5c444b75e83916df14018f13ffeb1a63d42d9194294497ed0a31b6a91bbf19745591aea659f86596072141ad67627189a71c1d09ac20b9e28665c5de51886b1
6
+ metadata.gz: ed7ba479c15c31262026550cd5f734cbede0304d3d62cb335deabcb4b590e9206bb6d9fb098b3a6843bac84ce49dff0ff8007d86c43f411e677fe6d2f3437921
7
+ data.tar.gz: 2c96a1aa18ee749816846d6681c51c345b58a7c9820b2c00b634b0bbe4bf23c716cae0773a554c1a99576b893d9724bc7c751c88f3aa185087817af2df0c6fba
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## 3.0.3
2
+ - Add protection for the (Java) Event when adding Twitter gem objects to the event in values,
3
+ fixes #40.
4
+ - add a specific rescue clause for any exceptions raised during event generation - these
5
+ should not be handled by the general rescue in the run method because this rescue assumes
6
+ a twitter gem upstream error and acts accordingly which is the wrong recovery action for an event generation failure
7
+ - refactor tests and add new integration test that publishes a tweet with known content.
8
+
1
9
  ## 3.0.2
2
10
  - Relax constraint on logstash-core-plugin-api to >= 1.60 <= 2.99
3
11
 
data/CONTRIBUTORS CHANGED
@@ -4,6 +4,7 @@ reports, or in general have helped logstash along its way.
4
4
  Contributors:
5
5
  * Bernd Ahlers (bernd)
6
6
  * Colin Surprenant (colinsurprenant)
7
+ * Guy Boertje (guyboertje)
7
8
  * John E. Vincent (lusis)
8
9
  * Jordan Sissel (jordansissel)
9
10
  * Pete Fritchman (fetep)
data/INTEGRATION.md ADDED
@@ -0,0 +1,33 @@
1
+ ### Attn: Logstash Team
2
+
3
+ To run integration tests locally, you will need to add a file called `integration_credentials.rb` to the spec folder.
4
+
5
+ This file is in gitignore.
6
+
7
+ The contents should contain this:
8
+ ```
9
+ if !defined?(ENV)
10
+ ENV = {}
11
+ end
12
+
13
+ ENV['PUB_TWITTER_CONSUMER_KEY'] = '...'
14
+ ENV['PUB_TWITTER_CONSUMER_SECRET'] = '...'
15
+ ENV['PUB_TWITTER_OAUTH_TOKEN'] = '...'
16
+ ENV['PUB_TWITTER_OAUTH_TOKEN_SECRET'] = '...'
17
+
18
+ ENV['TWITTER_CONSUMER_KEY'] = '...'
19
+ ENV['TWITTER_CONSUMER_SECRET'] = '...'
20
+ ENV['TWITTER_OAUTH_TOKEN'] = '...'
21
+ ENV['TWITTER_OAUTH_TOKEN_SECRET'] = '...'
22
+
23
+ ENV['CREDS_LOADED'] = 'twitter integration creds are loaded'
24
+ ```
25
+
26
+ Use the information in the Elastic WIKI at DEV Logstash Twitter: Integration keys for logstash-input-twitter
27
+ to fill in the `...` blanks.
28
+
29
+ LogstashCiPub maps to 'PUB_TWITTER\*'
30
+
31
+ LogstashCiSub maps to 'TWITTER\*'
32
+
33
+ The spec_helper has been changed to run the integration tests as well if this file is present.
@@ -10,7 +10,7 @@ require "logstash/inputs/twitter/patches"
10
10
  # Ingest events from the Twitter Streaming API.
11
11
  class LogStash::Inputs::Twitter < LogStash::Inputs::Base
12
12
 
13
- attr_reader :filter_options
13
+ attr_reader :filter_options, :event_generation_error_count
14
14
 
15
15
  config_name "twitter"
16
16
 
@@ -98,6 +98,11 @@ class LogStash::Inputs::Twitter < LogStash::Inputs::Base
98
98
  # Port where the proxy is listening, by default 3128 (squid)
99
99
  config :proxy_port, :validate => :number, :default => 3128
100
100
 
101
+ # Duration in seconds to wait before retrying a connection when twitter responds with a 429 TooManyRequests
102
+ # In some cases the 'x-rate-limit-reset' header is not set in the response and <error>.rate_limit.reset_in
103
+ # is nil. If this occurs then we use the integer specified here. The default is 5 minutes.
104
+ config :rate_limit_reset_in, :validate => :number, :default => 300
105
+
101
106
  def register
102
107
  require "twitter"
103
108
 
@@ -118,6 +123,11 @@ class LogStash::Inputs::Twitter < LogStash::Inputs::Base
118
123
 
119
124
  def run(queue)
120
125
  @logger.info("Starting twitter tracking", twitter_options.clone) # need to pass a clone as it modify this var otherwise
126
+
127
+ # keep track of the amount of non-specific errors rescued and logged - use in testing to verify no errors.
128
+ # this is because, as yet, we can't have rspec expectations on the logger instance.
129
+ @event_generation_error_count = 0
130
+
121
131
  begin
122
132
  if @use_samples
123
133
  @stream_client.sample do |tweet|
@@ -131,11 +141,13 @@ class LogStash::Inputs::Twitter < LogStash::Inputs::Base
131
141
  end
132
142
  end
133
143
  rescue Twitter::Error::TooManyRequests => e
134
- @logger.warn("Twitter too many requests error, sleeping for #{e.rate_limit.reset_in}s")
135
- Stud.stoppable_sleep(e.rate_limit.reset_in) { stop? }
144
+ sleep_for = e.rate_limit.reset_in || @rate_limit_reset_in # 5 minutes default value from config
145
+ @logger.warn("Twitter too many requests error, sleeping for #{sleep_for}s")
146
+ Stud.stoppable_sleep(sleep_for) { stop? }
136
147
  retry
137
148
  rescue => e
138
- @logger.warn("Twitter client error", :message => e.message, :exception => e, :backtrace => e.backtrace, :options => @filter_options)
149
+ # if a lot of these errors begin to occur, the repeated retry will result in TooManyRequests errors trapped above.
150
+ @logger.warn("Twitter client error", :message => e.message, :exception => e.class.name, :backtrace => e.backtrace, :options => @filter_options)
139
151
  retry
140
152
  end
141
153
  end # def run
@@ -155,11 +167,15 @@ class LogStash::Inputs::Twitter < LogStash::Inputs::Base
155
167
  private
156
168
 
157
169
  def tweet_processor(queue, tweet)
158
- if tweet.is_a?(Twitter::Tweet)
159
- return if ignore?(tweet)
160
- event = from_tweet(tweet)
161
- decorate(event)
162
- queue << event
170
+ if tweet.is_a?(Twitter::Tweet) && !ignore?(tweet)
171
+ begin
172
+ event = from_tweet(tweet)
173
+ decorate(event)
174
+ queue << event
175
+ rescue => e
176
+ @event_generation_error_count = @event_generation_error_count.next
177
+ @logger.error("Event generation error", :message => e.message, :exception => e.class.name, :backtrace => e.backtrace.take(10))
178
+ end
163
179
  end
164
180
  end
165
181
 
@@ -183,19 +199,20 @@ class LogStash::Inputs::Twitter < LogStash::Inputs::Base
183
199
  "source" => "http://twitter.com/#{tweet.user.screen_name}/status/#{tweet.id}"
184
200
  }
185
201
 
186
- attributes["hashtags"] = tweet.hashtags
187
- attributes["symbols"] = tweet.symbols
188
- attributes["user_mentions"] = tweet.user_mentions
189
-
202
+ attributes["hashtags"] = tweet.hashtags.map{|ht| ht.attrs}
203
+ attributes["symbols"] = tweet.symbols.map{|sym| sym.attrs}
204
+ attributes["user_mentions"] = tweet.user_mentions.map{|um| um.attrs}
190
205
  event = LogStash::Event.new(attributes)
191
- event.set("in-reply-to", tweet.in_reply_to_status_id) if tweet.reply?
206
+ if tweet.reply? && !tweet.in_reply_to_status_id.nil?
207
+ event.set("in-reply-to", tweet.in_reply_to_status_id)
208
+ end
192
209
  unless tweet.urls.empty?
193
210
  event.set("urls", tweet.urls.map(&:expanded_url).map(&:to_s))
194
211
  end
195
212
  end
196
213
 
197
214
  # Work around bugs in JrJackson. The standard serializer won't work till we upgrade
198
- event.set("in-reply-to", nil) if event.get("in-reply-to").is_a?(Twitter::NullObject)
215
+ # event.set("in-reply-to", nil) if event.get("in-reply-to").is_a?(Twitter::NullObject)
199
216
 
200
217
  event
201
218
  end
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
 
3
3
  s.name = 'logstash-input-twitter'
4
- s.version = '3.0.2'
4
+ s.version = '3.0.3'
5
5
  s.licenses = ['Apache License (2.0)']
6
6
  s.summary = "Read events from the twitter streaming api."
7
7
  s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
@@ -26,4 +26,5 @@ Gem::Specification.new do |s|
26
26
 
27
27
  s.add_development_dependency 'logstash-devutils'
28
28
  s.add_development_dependency 'logstash-codec-plain'
29
+ s.add_development_dependency 'rspec-sequencing'
29
30
  end
Binary file
@@ -38,7 +38,7 @@ describe LogStash::Inputs::Twitter do
38
38
 
39
39
  describe "when told to shutdown" do
40
40
  before(:each) do
41
- allow(Twitter::Streaming::Client).to receive(:new).and_return(MockClient.new)
41
+ allow(Twitter::Streaming::Client).to receive(:new).and_return(LogstashTwitterInput::MockClient.new)
42
42
  end
43
43
  it_behaves_like "an interruptible input plugin"
44
44
  end
@@ -67,7 +67,7 @@ describe LogStash::Inputs::Twitter do
67
67
 
68
68
  it "uses the sample endpoint" do
69
69
  expect(stream_client).to receive(:sample).once
70
- run_input_with(input, queue)
70
+ LogstashTwitterInput.run_input_with(input, queue)
71
71
  end
72
72
 
73
73
  end
@@ -133,7 +133,7 @@ describe LogStash::Inputs::Twitter do
133
133
 
134
134
  it "using the filter endpoint" do
135
135
  expect(stream_client).to receive(:filter).with(options).once
136
- run_input_with(input, queue)
136
+ LogstashTwitterInput.run_input_with(input, queue)
137
137
  end
138
138
 
139
139
  context "when not filtering retweets" do
@@ -158,7 +158,7 @@ describe LogStash::Inputs::Twitter do
158
158
  expect(input).to receive(:from_tweet).with(tweet)
159
159
  expect(stream_client).to receive(:filter).at_least(:once).and_yield(tweet)
160
160
  expect(queue).to receive(:<<)
161
- run_input_with(input, queue)
161
+ LogstashTwitterInput.run_input_with(input, queue)
162
162
  end
163
163
  end
164
164
 
@@ -183,7 +183,7 @@ describe LogStash::Inputs::Twitter do
183
183
  allow(tweet).to receive(:retweet?).and_return(true)
184
184
  expect(stream_client).to receive(:filter).at_least(:once).and_yield(tweet)
185
185
  expect(queue).not_to receive(:<<)
186
- run_input_with(input, queue)
186
+ LogstashTwitterInput.run_input_with(input, queue)
187
187
  end
188
188
  end
189
189
 
@@ -0,0 +1,86 @@
1
+ require_relative "../spec_helper"
2
+
3
+ Thread.abort_on_exception = true
4
+
5
+ describe LogStash::Inputs::Twitter do
6
+ describe "full integration", :integration => true do
7
+ let(:pub_config) do
8
+ {
9
+ :consumer_key => ENV['PUB_TWITTER_CONSUMER_KEY'],
10
+ :consumer_secret => ENV['PUB_TWITTER_CONSUMER_SECRET'],
11
+ :access_token => ENV['PUB_TWITTER_OAUTH_TOKEN'],
12
+ :access_token_secret => ENV['PUB_TWITTER_OAUTH_TOKEN_SECRET']
13
+ }
14
+ end
15
+
16
+ let(:publisher) do
17
+ Twitter::REST::Client.new(pub_config)
18
+ end
19
+
20
+ let(:plugin_cfg) do
21
+ {
22
+ 'consumer_key' => ENV['TWITTER_CONSUMER_KEY'],
23
+ 'consumer_secret' => ENV['TWITTER_CONSUMER_SECRET'],
24
+ 'oauth_token' => ENV['TWITTER_OAUTH_TOKEN'],
25
+ 'oauth_token_secret' => ENV['TWITTER_OAUTH_TOKEN_SECRET'],
26
+ 'keywords' => ['logstash_ci_publish'],
27
+ 'full_tweet' => full_tweets
28
+ }
29
+ end
30
+
31
+ let(:plugin) { described_class.new(plugin_cfg) }
32
+ let(:queue) { Array.new }
33
+ let(:tweet_count) { 1 }
34
+ let(:full_tweets) { false }
35
+
36
+ before do
37
+ Thread.new do
38
+ plugin.register
39
+ plugin.run(queue)
40
+ end
41
+ end
42
+
43
+ context "partial tweet pub sub" do
44
+ it "receives an event from the twitter stream" do
45
+ RSpec::Sequencing.run_after(1, "update tweet status") do
46
+ twt = "logstash_ci_publish partial tweet test #{Time.now} #{SecureRandom.hex(6)} $AAPL #lscipub @logstash https://www.elastic.co/downloads/logstash"
47
+ publisher.update_with_media(twt, File.open(LogstashTwitterInput.fixture("small_smile.png")))
48
+ end.then_after(3, "stop plugin") do
49
+ plugin.stop
50
+ true
51
+ end.value
52
+ expect(queue.size).to eq(tweet_count)
53
+ expect(plugin.event_generation_error_count).to eq(0)
54
+ event = queue.first
55
+ expect { event.to_json }.not_to raise_error
56
+ expect(event.get("hashtags").size).to be > 0
57
+ expect(event.get("symbols").size).to be > 0
58
+ expect(event.get("user_mentions").size).to be > 0
59
+ expect(event.get("urls").size).to be > 0
60
+ end
61
+ end
62
+
63
+ context "full tweet pub sub" do
64
+ let(:full_tweets) { true }
65
+
66
+ it "receives an event from the twitter stream" do
67
+ RSpec::Sequencing.run_after(0.1, "update tweet status") do
68
+ twt = "logstash_ci_publish full tweet test #{Time.now} #{SecureRandom.hex(6)} $AAPL #lscipub @logstash https://www.elastic.co/downloads/logstash"
69
+ publisher.update_with_media(twt, File.open(LogstashTwitterInput.fixture("small_smile.png")))
70
+ end.then_after(3, "stop plugin") do
71
+ plugin.stop
72
+ true
73
+ end.value
74
+ expect(queue.count).to eq(tweet_count)
75
+ expect(plugin.event_generation_error_count).to eq(0)
76
+ event = queue.first
77
+ expect { LogStash::Json.dump(event.to_hash) }.not_to raise_error
78
+ expect(event.get("[entities][hashtags]").size).to be > 0
79
+ expect(event.get("[entities][symbols]").size).to be > 0
80
+ expect(event.get("[entities][user_mentions]").size).to be > 0
81
+ expect(event.get("[entities][urls]").size).to be > 0
82
+ expect(event.get("[extended_tweet][entities][media]").size).to be > 0
83
+ end
84
+ end
85
+ end
86
+ end
@@ -1,8 +1,11 @@
1
+ __END__
2
+ # this test is quite unreliable - commenting it out
3
+
1
4
  require_relative "../spec_helper"
2
5
 
3
6
  describe LogStash::Inputs::Twitter do
4
7
 
5
- describe "#receive", :integration => true do
8
+ describe "#receive [integration]", :integration => true do
6
9
 
7
10
  context "keyword search" do
8
11
  let(:config) do
@@ -13,7 +16,7 @@ describe LogStash::Inputs::Twitter do
13
16
  consumer_secret => '#{ENV['TWITTER_CONSUMER_SECRET']}'
14
17
  oauth_token => '#{ENV['TWITTER_OAUTH_TOKEN']}'
15
18
  oauth_token_secret => '#{ENV['TWITTER_OAUTH_TOKEN_SECRET']}'
16
- keywords => [ 'Barcelona' ]
19
+ keywords => [ 'London', 'Politics', 'New York', 'Samsung', 'Apple' ]
17
20
  full_tweet => true
18
21
  }
19
22
  }
@@ -103,4 +106,4 @@ describe LogStash::Inputs::Twitter do
103
106
  end
104
107
  end
105
108
 
106
- end
109
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,18 +1,40 @@
1
- require "logstash/devutils/rspec/spec_helper"
1
+ require 'logstash/devutils/rspec/spec_helper'
2
2
  require 'logstash/inputs/twitter'
3
3
  require 'twitter'
4
+ require 'rspec_sequencing'
4
5
 
5
- class MockClient
6
- def filter(options)
7
- loop { yield }
6
+ creds_file_path = File.expand_path('spec/integration_credentials.rb')
7
+ if File.exist?(creds_file_path)
8
+ load creds_file_path
9
+ RSpec.configure do |config|
10
+ # enable integrations only when credentials are loaded
11
+ exclusions = config.exclusion_filter
12
+ exclusions.delete(:integration)
13
+ config.exclusion_filter = exclusions
8
14
  end
9
- alias_method :sample, :filter
10
15
  end
11
16
 
12
- def run_input_with(input, queue)
13
- t = Thread.new(input, queue) do |_input, _queue|
14
- _input.run(_queue)
17
+ module LogstashTwitterInput
18
+ class MockClient
19
+ def filter(options)
20
+ loop { yield }
21
+ end
22
+ alias_method :sample, :filter
15
23
  end
16
- sleep 0.1
17
- t.kill
18
- end
24
+
25
+ def self.run_input_with(input, queue)
26
+ t = Thread.new(input, queue) do |_input, _queue|
27
+ _input.run(_queue)
28
+ end
29
+ sleep 0.1
30
+ t.kill
31
+ end
32
+
33
+ def self.fixture_path
34
+ File.expand_path('../fixtures', __FILE__)
35
+ end
36
+
37
+ def self.fixture(file)
38
+ File.new(self.fixture_path + '/' + file)
39
+ end
40
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-input-twitter
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.2
4
+ version: 3.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elastic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-07-14 00:00:00.000000000 Z
11
+ date: 2016-10-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -92,6 +92,20 @@ dependencies:
92
92
  - - ">="
93
93
  - !ruby/object:Gem::Version
94
94
  version: '0'
95
+ - !ruby/object:Gem::Dependency
96
+ requirement: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ name: rspec-sequencing
102
+ prerelease: false
103
+ type: :development
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
95
109
  description: This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program
96
110
  email: info@elastic.co
97
111
  executables: []
@@ -101,13 +115,16 @@ files:
101
115
  - CHANGELOG.md
102
116
  - CONTRIBUTORS
103
117
  - Gemfile
118
+ - INTEGRATION.md
104
119
  - LICENSE
105
120
  - NOTICE.TXT
106
121
  - README.md
107
122
  - lib/logstash/inputs/twitter.rb
108
123
  - lib/logstash/inputs/twitter/patches.rb
109
124
  - logstash-input-twitter.gemspec
125
+ - spec/fixtures/small_smile.png
110
126
  - spec/inputs/twitter_spec.rb
127
+ - spec/integration/twitter_pubsub_spec.rb
111
128
  - spec/integration/twitter_spec.rb
112
129
  - spec/spec_helper.rb
113
130
  homepage: http://www.elastic.co/guide/en/logstash/current/index.html
@@ -132,11 +149,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
132
149
  version: '0'
133
150
  requirements: []
134
151
  rubyforge_project:
135
- rubygems_version: 2.6.3
152
+ rubygems_version: 2.4.8
136
153
  signing_key:
137
154
  specification_version: 4
138
155
  summary: Read events from the twitter streaming api.
139
156
  test_files:
157
+ - spec/fixtures/small_smile.png
140
158
  - spec/inputs/twitter_spec.rb
159
+ - spec/integration/twitter_pubsub_spec.rb
141
160
  - spec/integration/twitter_spec.rb
142
161
  - spec/spec_helper.rb