fluent-plugin-feedly 0.0.1

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.
@@ -0,0 +1,5 @@
1
+ /.bundle/
2
+ /Gemfile.lock
3
+ /pkg/
4
+ /tmp/
5
+ /vendor/
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.3
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in fluent-plugin-feedly.gemspec
4
+ gemspec
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2012- Kentaro Yoshida
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
@@ -0,0 +1,135 @@
1
+ # fluent-plugin-feedly
2
+
3
+ ## Overview
4
+
5
+ Fluentd input plugin to fetch RSS/ATOM feed via Feedly Could.
6
+
7
+ ## Dependencies
8
+
9
+ * Ruby 1.9.3+
10
+ * Fluentd 0.10.54+
11
+
12
+ ## Installation
13
+
14
+ install with gem or fluent-gem command as:
15
+
16
+ `````
17
+ # for system installed fluentd
18
+ $ gem install fluent-plugin-feedly
19
+
20
+ # for td-agent
21
+ $ sudo /usr/lib64/fluent/ruby/bin/fluent-gem install fluent-plugin-feedly
22
+ `````
23
+
24
+ ## Configuration
25
+
26
+ `````
27
+ <source>
28
+ type feedly
29
+
30
+ # Set feedly access token
31
+ # ref. https://feedly.com/v3/auth/dev
32
+ access_token ACCESS_TOKEN # Required
33
+
34
+ # Set file-path to store last fetched article position
35
+ state_file /var/log/td-agent/feedly.state # Required
36
+
37
+ # Set output tag
38
+ tag input.feedly # Required
39
+
40
+ # List subscribe categories in your feedly account with JSON Array
41
+ subscribe_categories ["global.all"] # Optional (default: global.all)
42
+
43
+ # Set update checking frequency
44
+ run_interval 30m # Optional (default: 10m)
45
+
46
+ # Set bulk read size
47
+ fetch_count 20 # Optional (default: 20)
48
+
49
+ # fetching range of time within 30d
50
+ fetch_time_range 3d # Optional (default: 3d)
51
+
52
+ # fetching range of time for initial startup within 30d
53
+ fetch_time_range_on_startup 2w # Optional (default: 2w)
54
+
55
+ # Using sandbox account
56
+ enable_sandbox false # Optional (default: false)
57
+
58
+ # Set log level for this plugin. To see debug level, set 'debug' for this value.
59
+ # it can see at stdout as like `$ tail -f /var/log/td-agent/td-agent.log`
60
+ log_level info        # Optional (default: info)
61
+ </source>
62
+ `````
63
+
64
+ **note** : The `subscribe_categories` is also supported with single or multi line configuration like below.
65
+
66
+ ```
67
+ # single line
68
+ subscribe_categories ["先端技術", "mysql"]
69
+
70
+ # multi line
71
+ subscribe_categories [
72
+ "先端技術",
73
+ "mysql"
74
+ ]
75
+ ```
76
+
77
+ ## Usage
78
+
79
+ After installed this plugin, executing fluentd with following configuration.
80
+
81
+ ```
82
+ $ cat /etc/td-agent/td-agent.conf
83
+ <source>
84
+ type feedly
85
+ access_token YOUR_ACCESS_TOKEN
86
+ state_file /var/log/td-agent/feedly.state
87
+ tag input.feedly
88
+ run_interval 30m
89
+ fetch_time_range 1h
90
+ fetch_time_range_on_startup 3h
91
+ log_level debug
92
+ </source>
93
+
94
+ <match input.feedly>
95
+ type file
96
+ path /tmp/feedly*.json
97
+ symlink_path /tmp/feedly.json
98
+ format json
99
+ append true
100
+ </match>
101
+ ```
102
+
103
+ You can see the behavior about this plugin with this command.
104
+
105
+ ```
106
+ # to check stdout of this plugin
107
+ $ tail -f /var/log/td-agent/td-agent.log
108
+ 2014-10-16 14:47:01 +0900 [debug]: Feedly: fetched articles. articles=416 request_option={:count=>1000, :continuation=>"148cfb7f516:9371a3c:726280cf", :newerThan=>1412228787000}
109
+ 2014-10-16 15:02:02 +0900 [debug]: Feedly: fetched articles. articles=492 request_option={:count=>1000, :continuation=>nil, :newerThan=>1413428521000}
110
+ ```
111
+
112
+ ```
113
+ # to check fetched articles
114
+ $ tail -f /tmp/feedly.json | jq "."
115
+ ```
116
+
117
+ ## TODO
118
+
119
+ Pull requests are very welcome!!
120
+
121
+ ## Copyright
122
+
123
+ Copyright © 2014- Kentaro Yoshida ([@yoshi_ken](https://twitter.com/yoshi_ken))
124
+
125
+ ## License
126
+
127
+ Apache License, Version 2.0
128
+
129
+ ## Contributing
130
+
131
+ 1. Fork it ( https://github.com/[my-github-username]/fluent-plugin-feedly/fork )
132
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
133
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
134
+ 4. Push to the branch (`git push origin my-new-feature`)
135
+ 5. Create a new Pull Request
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+ Rake::TestTask.new(:test) do |test|
4
+ test.libs << 'lib' << 'test'
5
+ test.pattern = 'test/**/test_*.rb'
6
+ test.verbose = true
7
+ end
8
+
9
+ task :default => :test
10
+
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "fluent-plugin-feedly"
7
+ #spec.version = Fluent::Plugin::Feedly::VERSION
8
+ spec.version = "0.0.1"
9
+ spec.authors = ["Kentaro Yoshida"]
10
+ spec.email = ["y.ken.studio@gmail.com"]
11
+ spec.summary = %q{Fluentd input plugin to fetch RSS/ATOM feed via Feedly Could.}
12
+ #spec.description = %q{TODO: Write a longer description. Optional.}
13
+ spec.homepage = "https://github.com/y-ken/fluent-plugin-feedly"
14
+ spec.license = "Apache 2.0"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency "fluentd"#, "~> 0.10.54"
22
+ spec.add_runtime_dependency "feedlr"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.7"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ end
@@ -0,0 +1,148 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module Fluent
4
+ class FeedlyInput < Fluent::Input
5
+ Plugin.register_input('feedly', self)
6
+
7
+ config_param :access_token, :string
8
+ config_param :state_file, :string
9
+ config_param :tag, :string
10
+
11
+ config_param :subscribe_categories, :array, :default => ['global.all']
12
+ config_param :run_interval, :time, :default => 60*10 #10m
13
+ config_param :fetch_count, :integer, :default => 20
14
+ config_param :fetch_time_range, :time, :default => 60*60*24*3 #3d
15
+ config_param :fetch_time_range_on_startup, :time, :default => 60*60*24*14 #2w
16
+ config_param :enable_sandbox, :bool, :default => false
17
+
18
+ unless method_defined?(:log)
19
+ define_method(:log) { $log }
20
+ end
21
+
22
+ def initialize
23
+ require 'feedlr'
24
+ require 'digest/sha2'
25
+
26
+ super
27
+ end
28
+
29
+ def configure(conf)
30
+ super
31
+
32
+ if not @fetch_count >= 20 && @fetch_count <= 10000
33
+ raise Fluent::ConfigError, "Feedly: fetch_count param (#{@fetch_count}) should be between 20 and 10000."
34
+ end
35
+
36
+ @client = Feedlr::Client.new(
37
+ oauth_access_token: @access_token,
38
+ sandbox: @enable_sandbox,
39
+ )
40
+ end
41
+
42
+ def start
43
+ @profile_id = @client.user_profile.id
44
+ @state_store = StateStore.new(@state_file)
45
+ @thread = Thread.new(&method(:run))
46
+ end
47
+
48
+ def shutdown
49
+ Thread.kill(@thread)
50
+ end
51
+
52
+ def run
53
+ @initial_loop = true
54
+ loop do
55
+ begin
56
+ fetch
57
+ rescue => e
58
+ log.error "Feedly: unexpected error has occoured.", :error => e.message, :error_class => e.class
59
+ log.error_backtrace e.backtrace
60
+ sleep @run_interval
61
+ retry
62
+ end
63
+ sleep @run_interval
64
+ end
65
+ end
66
+
67
+ def fetch
68
+ @subscribe_categories.each do |category_name|
69
+ category_id = "user/#{@profile_id}/category/#{category_name}"
70
+ fetch_time_range = get_fetch_time_range
71
+ loop {
72
+ request_option = { count: @fetch_count, continuation: get_continuation_id, newerThan: fetch_time_range }
73
+ cursor = @client.stream_entries_contents(category_id, request_option)
74
+ cursor.items.each do |item|
75
+ Engine.emit(@tag, Engine.now, item)
76
+ end
77
+ log.debug "Feedly: fetched articles.", articles: cursor.items.size, request_option: request_option
78
+ set_continuation_id(cursor.continuation)
79
+ break if get_continuation_id.nil?
80
+ }
81
+ end
82
+ end
83
+
84
+ def get_fetch_time_range
85
+ if @initial_loop
86
+ @initial_loop = false
87
+ range = @fetch_time_range_on_startup
88
+ else
89
+ range = @fetch_time_range
90
+ end
91
+ return (Time.now.to_i - range ) * 1000
92
+ end
93
+
94
+ def subscribe_categories_hash
95
+ Digest::SHA512.digest(@subscribe_categories.sort.join(''))
96
+ end
97
+
98
+ def set_continuation_id(continuation_id)
99
+ @state_store.set("continuation", {
100
+ id: continuation_id,
101
+ subscribe_categories_hash: subscribe_categories_hash
102
+ })
103
+ @state_store.update!
104
+ end
105
+
106
+ def get_continuation_id
107
+ record = @state_store.get('continuation')
108
+ if subscribe_categories_hash == record[:subscribe_categories_hash]
109
+ return record[:id]
110
+ else
111
+ return nil
112
+ end
113
+ end
114
+
115
+ # implementation has copied from its code.
116
+ # https://github.com/fluent/fluent-plugin-sql/blob/master/lib/fluent/plugin/in_sql.rb
117
+ class StateStore
118
+ def initialize(path)
119
+ @path = path
120
+ if File.exists?(@path)
121
+ @data = YAML.load_file(@path)
122
+ if @data == false || @data == []
123
+ # this happens if an users created an empty file accidentally
124
+ @data = {}
125
+ elsif !@data.is_a?(Hash)
126
+ raise "state_file on #{@path.inspect} is invalid"
127
+ end
128
+ else
129
+ @data = {}
130
+ end
131
+ end
132
+
133
+ def set(key, data)
134
+ @data.store(key.to_sym, data)
135
+ end
136
+
137
+ def get(key)
138
+ @data[key.to_sym] ||= {}
139
+ end
140
+
141
+ def update!
142
+ File.open(@path, 'w') {|f|
143
+ f.write YAML.dump(@data)
144
+ }
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,28 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+
12
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
13
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
14
+ require 'fluent/test'
15
+ unless ENV.has_key?('VERBOSE')
16
+ nulllogger = Object.new
17
+ nulllogger.instance_eval {|obj|
18
+ def method_missing(method, *args)
19
+ # pass
20
+ end
21
+ }
22
+ $log = nulllogger
23
+ end
24
+
25
+ require 'fluent/plugin/in_feedly'
26
+
27
+ class Test::Unit::TestCase
28
+ end
@@ -0,0 +1,30 @@
1
+ require 'helper'
2
+
3
+ class FeedlyInputTest < Test::Unit::TestCase
4
+ def setup
5
+ Fluent::Test.setup
6
+ end
7
+
8
+ CONFIG = %[
9
+ access_token YOUR_ACCESS_TOKEN
10
+ state_file /var/log/td-agent/feedly.state
11
+ tag input.feedly
12
+ run_interval 30m
13
+ ]
14
+
15
+ def create_driver(conf=CONFIG,tag='test')
16
+ Fluent::Test::OutputTestDriver.new(Fluent::FeedlyInput, tag).configure(conf)
17
+ end
18
+
19
+ def test_configure
20
+ assert_raise(Fluent::ConfigError) {
21
+ d = create_driver('')
22
+ }
23
+ d = create_driver(CONFIG)
24
+ assert_equal 'YOUR_ACCESS_TOKEN', d.instance.access_token
25
+ assert_equal '/var/log/td-agent/feedly.state', d.instance.state_file
26
+ assert_equal 1800, d.instance.run_interval
27
+ assert_equal 'input.feedly', d.instance.tag
28
+ end
29
+ end
30
+
metadata ADDED
@@ -0,0 +1,122 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fluent-plugin-feedly
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Kentaro Yoshida
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-10-16 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: fluentd
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: feedlr
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: bundler
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '1.7'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.7'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rake
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '10.0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: '10.0'
78
+ description:
79
+ email:
80
+ - y.ken.studio@gmail.com
81
+ executables: []
82
+ extensions: []
83
+ extra_rdoc_files: []
84
+ files:
85
+ - .gitignore
86
+ - .travis.yml
87
+ - Gemfile
88
+ - LICENSE.txt
89
+ - README.md
90
+ - Rakefile
91
+ - fluent-plugin-feedly.gemspec
92
+ - lib/fluent/plugin/in_feedly.rb
93
+ - test/helper.rb
94
+ - test/plugin/test_in_mysql.rb
95
+ homepage: https://github.com/y-ken/fluent-plugin-feedly
96
+ licenses:
97
+ - Apache 2.0
98
+ post_install_message:
99
+ rdoc_options: []
100
+ require_paths:
101
+ - lib
102
+ required_ruby_version: !ruby/object:Gem::Requirement
103
+ none: false
104
+ requirements:
105
+ - - ! '>='
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ none: false
110
+ requirements:
111
+ - - ! '>='
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ requirements: []
115
+ rubyforge_project:
116
+ rubygems_version: 1.8.23
117
+ signing_key:
118
+ specification_version: 3
119
+ summary: Fluentd input plugin to fetch RSS/ATOM feed via Feedly Could.
120
+ test_files:
121
+ - test/helper.rb
122
+ - test/plugin/test_in_mysql.rb