fluent-plugin-github-activities 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -17,86 +17,66 @@
17
17
  # License along with fluent-plugin-github-activities. If not, see
18
18
  # <http://www.gnu.org/licenses/>.
19
19
 
20
- require "pathname"
21
20
  require "json"
22
21
 
23
- require "fluent/plugin/github-activities/safe_file_writer"
24
-
25
22
  module Fluent
26
- module GithubActivities
27
- class UsersManager
28
- DEFAULT_LAST_EVENT_TIMESTAMP = -1
29
-
30
- def initialize(params={})
31
- @users = params[:users]
23
+ module Plugin
24
+ module GithubActivities
25
+ class UsersManager
26
+ DEFAULT_LAST_EVENT_TIMESTAMP = -1
32
27
 
33
- @positions = {}
34
- @pos_file = params[:pos_file]
35
- @pos_file = Pathname(@pos_file) if @pos_file
36
- end
37
-
38
- def generate_initial_requests
39
- @users.collect do |user|
40
- new_events_request(user)
28
+ def initialize(params={})
29
+ @users = params[:users]
30
+ @pos_storage = params[:pos_storage]
41
31
  end
42
- end
43
32
 
44
- def new_events_request(user, options={})
45
- request = {
46
- :type => TYPE_EVENTS,
47
- :user => user,
48
- }
49
- response = options[:previous_response]
50
- if response
51
- now = options[:now] || Time.now
52
- interval = response["X-Poll-Interval"].to_i
53
- time_to_process = now.to_i + interval
54
- request[:previous_entity_tag] = response["ETag"] ||
55
- options[:previous_entity_tag]
56
- request[:process_after] = time_to_process
57
- else
58
- request[:previous_entity_tag] = options[:previous_entity_tag] if options.key?(:previous_entity_tag)
33
+ def generate_initial_requests
34
+ @users.collect do |user|
35
+ new_events_request(user)
36
+ end
59
37
  end
60
- request
61
- end
62
-
63
- def position_for(user)
64
- load_positions
65
- @positions[user]
66
- end
67
38
 
68
- def save_position_for(user, params)
69
- load_positions
70
- @positions[user] ||= {}
71
-
72
- if params[:entity_tag]
73
- @positions[user]["entity_tag"] = params[:entity_tag]
39
+ def new_events_request(user, options={})
40
+ request = {
41
+ type: TYPE_EVENTS,
42
+ user: user,
43
+ }
44
+ response = options[:previous_response]
45
+ if response
46
+ now = options[:now] || Time.now
47
+ interval = response["X-Poll-Interval"].to_i
48
+ time_to_process = now.to_i + interval
49
+ request[:previous_entity_tag] = response["ETag"] ||
50
+ options[:previous_entity_tag]
51
+ request[:process_after] = time_to_process
52
+ else
53
+ request[:previous_entity_tag] = options[:previous_entity_tag] if options.key?(:previous_entity_tag)
54
+ end
55
+ request
74
56
  end
75
57
 
76
- if params[:last_event_timestamp] and
77
- params[:last_event_timestamp] != DEFAULT_LAST_EVENT_TIMESTAMP
78
- old_timestamp = @positions[user]["last_event_timestamp"]
79
- if old_timestamp.nil? or old_timestamp < params[:last_event_timestamp]
80
- @positions[user]["last_event_timestamp"] = params[:last_event_timestamp]
81
- end
58
+ def position_for(user)
59
+ @pos_storage.get(user)
82
60
  end
83
61
 
84
- save_positions
85
- end
62
+ def save_position_for(user, params)
63
+ position = @pos_storage.get(user) || {}
86
64
 
87
- private
88
- def load_positions
89
- return unless @pos_file
90
- return unless @pos_file.exist?
65
+ if params[:entity_tag]
66
+ position["entity_tag"] = params[:entity_tag]
67
+ end
91
68
 
92
- @positions = JSON.parse(@pos_file.read)
93
- rescue
94
- @positions = {}
95
- end
69
+ if params[:last_event_timestamp] and
70
+ params[:last_event_timestamp] != DEFAULT_LAST_EVENT_TIMESTAMP
71
+ old_timestamp = position["last_event_timestamp"]
72
+ if old_timestamp.nil? or old_timestamp < params[:last_event_timestamp]
73
+ position["last_event_timestamp"] = params[:last_event_timestamp]
74
+ end
75
+ end
96
76
 
97
- def save_positions
98
- return unless @pos_file
99
- SafeFileWriter.write(@pos_file, JSON.pretty_generate(@positions))
77
+ @pos_storage.put(user, position)
78
+ @pos_storage.save
79
+ end
100
80
  end
101
81
  end
102
82
  end
@@ -17,98 +17,116 @@
17
17
  # License along with fluent-plugin-github-activities. If not, see
18
18
  # <http://www.gnu.org/licenses/>.
19
19
 
20
- module Fluent
21
- class GithubActivitiesInput < Input
22
- DEFAULT_BASE_TAG = "github-activity"
23
- DEFAULT_CLIENTS = 4
24
-
25
- Plugin.register_input("github-activities", self)
26
-
27
- config_param :access_token, :string, :default => nil, :secret => true
28
- config_param :users, :string, :default => nil
29
- config_param :users_list, :string, :default => nil
30
- config_param :include_commits_from_pull_request, :bool, :default => false
31
- config_param :include_foreign_commits, :bool, :default => false
32
- config_param :base_tag, :string, :default => DEFAULT_BASE_TAG
33
- config_param :pos_file, :string, :default => nil
34
- config_param :clients, :integer, :default => DEFAULT_CLIENTS
35
- config_param :interval, :integer, :default => 1
36
-
37
- def initialize
38
- super
39
-
40
- require "thread"
41
- require "pathname"
42
- require "fluent/plugin/github-activities"
43
- end
44
-
45
- def start
46
- @base_tag = @base_tag.sub(/\.\z/, "")
47
-
48
- users = prepare_users_list
49
- n_clients = [@clients, users.size].min
50
- @interval = @interval * n_clients
20
+ require "thread"
21
+ require "pathname"
22
+ require "fluent/plugin/input"
23
+ require "fluent/plugin/github-activities"
51
24
 
52
- @client_threads = []
53
- @request_queue = Queue.new
25
+ module Fluent
26
+ module Plugin
27
+ class GithubActivitiesInput < Fluent::Plugin::Input
28
+ DEFAULT_BASE_TAG = "github-activity"
29
+ DEFAULT_CLIENTS = 4
30
+ DEFAULT_STORAGE_TYPE = "local"
31
+
32
+ helpers :thread, :storage
33
+
34
+ Fluent::Plugin.register_input("github-activities", self)
35
+
36
+ config_param :access_token, :string, default: nil, secret: true
37
+ config_param :users, :array, default: []
38
+ config_param :users_list, :string, default: nil
39
+ config_param :include_commits_from_pull_request, :bool, default: false
40
+ config_param :include_foreign_commits, :bool, default: false
41
+ config_param :base_tag, :string, default: DEFAULT_BASE_TAG
42
+ config_param :pos_file, :string, default: nil, obsoleted: "Use storage instead."
43
+ config_param :clients, :integer, default: DEFAULT_CLIENTS
44
+ config_param :interval, :integer, default: 1
45
+
46
+ config_section :storage do
47
+ config_set_default :usage, "in-github-activities"
48
+ config_set_default :@type, DEFAULT_STORAGE_TYPE
49
+ end
54
50
 
55
- users_manager_params = {
56
- :users => users,
57
- :pos_file => @pos_file,
58
- }
59
- users_manager = ::Fluent::GithubActivities::UsersManager.new(users_manager_params)
60
- users_manager.generate_initial_requests.each do |request|
61
- @request_queue.push(request)
51
+ def configure(conf)
52
+ super
53
+
54
+ @base_tag = @base_tag.sub(/\.\z/, "")
55
+ @users += load_users_list
56
+ @n_clients = [@clients, @users.size].min
57
+ @interval = @interval * @n_clients
58
+ raise Fluent::ConfigError, "You can define <storage> section at once" unless @storage_configs.size == 1
59
+ storage_section = @storage_configs.first
60
+ storage_config = storage_section.corresponding_config_element
61
+ @pos_storage = storage_create(usage: storage_section.usage,
62
+ conf: storage_config,
63
+ default_type: DEFAULT_STORAGE_TYPE)
62
64
  end
63
65
 
64
- n_clients.times do
65
- @client_threads << Thread.new do
66
- crawler_options = {
67
- :access_token => @access_token,
68
- :watching_users => users,
69
- :include_commits_from_pull_request => @include_commits_from_pull_request,
70
- :include_foreign_commits => @include_foreign_commits,
71
- :pos_file => @pos_file,
72
- :request_queue => @request_queue,
73
- :default_interval => @interval,
74
- }
75
- crawler = ::Fluent::GithubActivities::Crawler.new(crawler_options)
76
- crawler.on_emit = lambda do |tag, record|
77
- router.emit("#{@base_tag}.#{tag}", Engine.now, record)
78
- end
66
+ def start
67
+ super
68
+
69
+ @request_queue = Queue.new
70
+ @crawlers = []
79
71
 
80
- loop do
81
- crawler.process_request
82
- sleep(crawler.interval_for_next_request)
72
+ users_manager_params = {
73
+ users: @users,
74
+ pos_storage: @pos_storage,
75
+ }
76
+ users_manager = ::Fluent::Plugin::GithubActivities::UsersManager.new(users_manager_params)
77
+ users_manager.generate_initial_requests.each do |request|
78
+ @request_queue.push(request)
79
+ end
80
+ @n_clients.times do |n|
81
+ thread_create("in_github_activity_#{n}".to_sym) do
82
+ crawler_options = {
83
+ access_token: @access_token,
84
+ watching_users: @users,
85
+ include_commits_from_pull_request: @include_commits_from_pull_request,
86
+ include_foreign_commits: @include_foreign_commits,
87
+ pos_storage: @pos_storage,
88
+ request_queue: @request_queue,
89
+ default_interval: @interval,
90
+ log: log
91
+ }
92
+ crawler = ::Fluent::Plugin::GithubActivities::Crawler.new(crawler_options)
93
+ @crawlers << crawler
94
+ crawler.on_emit = lambda do |tag, record|
95
+ router.emit("#{@base_tag}.#{tag}", Engine.now, record)
96
+ end
97
+
98
+ loop do
99
+ crawler.process_request
100
+ break unless crawler.running
101
+ sleep(crawler.interval_for_next_request)
102
+ end
83
103
  end
84
104
  end
85
105
  end
86
- end
87
106
 
88
- def shutdown
89
- @client_threads.each(&:exit)
90
- end
107
+ def shutdown
108
+ until @request_queue.empty?
109
+ log.debug(queue_size: @request_queue.size)
110
+ sleep(@interval)
111
+ end
112
+ @crawlers.each(&:stop)
113
+ super
114
+ end
91
115
 
92
- private
93
- def prepare_users_list
94
- @users ||= ""
95
- users = @users.split(",")
116
+ private
96
117
 
97
- if @users_list
98
- users_list = Pathname(@users_list)
99
- if users_list.exist?
100
- list = users_list.read
101
- users += list.split("\n")
118
+ def load_users_list
119
+ users = []
120
+ if @users_list
121
+ users_list = Pathname(@users_list)
122
+ if users_list.exist?
123
+ list = users_list.read
124
+ users += list.split("\n")
125
+ end
102
126
  end
103
- end
104
127
 
105
- users = users.collect do |user|
106
- user.strip
107
- end.reject do |user|
108
- user.empty?
128
+ users.collect(&:strip).reject(&:empty?)
109
129
  end
110
-
111
- users
112
130
  end
113
131
  end
114
132
  end
@@ -0,0 +1,47 @@
1
+ [
2
+ {
3
+ "id": "2823041920",
4
+ "type": "PushEvent",
5
+ "actor": {
6
+ "id": 70062,
7
+ "login": "piroor",
8
+ "gravatar_id": "",
9
+ "url": "https://api.github.com/users/piroor",
10
+ "avatar_url": "https://avatars.githubusercontent.com/u/70062?"
11
+ },
12
+ "repo": {
13
+ "id": 35922279,
14
+ "name": "clear-code/fluent-plugin-github-activities",
15
+ "url": "https://api.github.com/repos/clear-code/fluent-plugin-github-activities"
16
+ },
17
+ "payload": {
18
+ "push_id": 670478406,
19
+ "size": 1,
20
+ "distinct_size": 1,
21
+ "ref": "refs/heads/master",
22
+ "head": "c908f319c7b6d5c5a69c8b675bde40dd990ee364",
23
+ "before": "e77acc068b5568f8ea55605db2e3a9005805c898",
24
+ "commits": [
25
+ {
26
+ "sha": "8e90721ff5d89f52b5b3adf0b86db01f03dc5588",
27
+ "author": {
28
+ "email": "shimoda@clear-code.com",
29
+ "name": "YUKI Hiroshi"
30
+ },
31
+ "message": "Add missing constant",
32
+ "distinct": true,
33
+ "url": "https://api.github.com/repos/clear-code/fluent-plugin-github-activities/commits/8e90721ff5d89f52b5b3adf0b86db01f03dc5588"
34
+ }
35
+ ]
36
+ },
37
+ "public": true,
38
+ "created_at": "2015-05-21T05:37:34Z",
39
+ "org": {
40
+ "id": 176515,
41
+ "login": "clear-code",
42
+ "gravatar_id": "",
43
+ "url": "https://api.github.com/orgs/clear-code",
44
+ "avatar_url": "https://avatars.githubusercontent.com/u/176515?"
45
+ }
46
+ }
47
+ ]
@@ -0,0 +1,2 @@
1
+ okkez
2
+ cosmo0920
@@ -0,0 +1,85 @@
1
+ require "fluent/plugin/in_github-activities"
2
+ require "fluent/test"
3
+ require "fluent/test/helpers"
4
+ require "fluent/test/driver/input"
5
+
6
+ class TestGithubActivitiesInput < Test::Unit::TestCase
7
+ include Fluent::Test::Helpers
8
+
9
+ setup do
10
+ Fluent::Test.setup
11
+ end
12
+
13
+ def create_driver(conf)
14
+ Fluent::Test::Driver::Input.new(Fluent::Plugin::GithubActivitiesInput).configure(conf)
15
+ end
16
+
17
+ sub_test_case "configure" do
18
+ test "empty" do
19
+ d = create_driver(config_element)
20
+ plugin = d.instance
21
+ storage = plugin._storages["in-github-activities"].storage
22
+ assert { storage.path.nil? }
23
+ assert { !storage.persistent }
24
+ end
25
+
26
+ test "obsoleted pos_file" do
27
+ conf = config_element("ROOT", "", { "pos_file" => "/tmp/pos_file.json" })
28
+ assert_raise Fluent::ObsoletedParameterError do
29
+ create_driver(conf)
30
+ end
31
+ end
32
+
33
+ data("end with .json" => ["/tmp/test.json", "/tmp/test.json"],
34
+ "end with .pos" => ["/tmp/test.pos", "/tmp/test.pos/worker0/storage.json"])
35
+ test "persistent storage with path" do |(path, expected_path)|
36
+ storage_conf = config_element(
37
+ "storage",
38
+ "in-github-activities",
39
+ { "@type" => "local", "persistent" => true, "path" => path }
40
+ )
41
+ conf = config_element("ROOT", "", {}, [storage_conf])
42
+ d = create_driver(conf)
43
+ plugin = d.instance
44
+ storage = plugin._storages["in-github-activities"].storage
45
+ assert_equal(expected_path, storage.path)
46
+ assert { storage.persistent }
47
+ end
48
+
49
+ data(single: ["piroor", ["piroor"]],
50
+ multiple: ["okkez,cosmo0920", ["okkez", "cosmo0920"]])
51
+ test "users" do |(users, expected_users)|
52
+ config = config_element("ROOT", "", { "users" => users })
53
+ d = create_driver(config)
54
+ plugin = d.instance
55
+ assert_equal(expected_users, plugin.users)
56
+ end
57
+
58
+ test "users_list" do
59
+ config = config_element("ROOT", "", { "users_list" => fixture_path("users.txt") })
60
+ d = create_driver(config)
61
+ plugin = d.instance
62
+ assert_equal(["okkez", "cosmo0920"], plugin.users)
63
+ end
64
+ end
65
+
66
+ sub_test_case "emit" do
67
+ test "simple" do
68
+ user_events_template = Addressable::Template.new("https://api.github.com/users/{user}/events/public")
69
+ stub_request(:get, user_events_template)
70
+ .to_return(body: File.open(fixture_path("piroor-events.json")), status: 200)
71
+ commits_template = Addressable::Template.new("https://api.github.com/repos/{owner}/{project}/commits/{commit_hash}")
72
+ stub_request(:get, commits_template)
73
+ .to_return(body: File.open(fixture_path("commit.json")), status: 200)
74
+ config = config_element("ROOT", "", { "users" => "piroor", "@log_level" => "trace" })
75
+ d = create_driver(config)
76
+ d.run(timeout: 1, expect_emits: 2)
77
+ tag, _time, record = d.events[0]
78
+ assert_equal("github-activity.commit", tag)
79
+ assert_equal("8e90721ff5d89f52b5b3adf0b86db01f03dc5588", record["sha"])
80
+ tag, _time, record = d.events[1]
81
+ assert_equal("github-activity.push", tag)
82
+ assert_equal("2823041920", record["id"])
83
+ end
84
+ end
85
+ end