logstash-input-twitter 3.0.2 → 3.0.3

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: 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