lita-pagerduty 0.2.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +1 -1
  3. data/.rubocop.yml +6 -5
  4. data/README.md +11 -9
  5. data/Rakefile +4 -2
  6. data/lib/exceptions.rb +11 -0
  7. data/lib/lita-pagerduty.rb +7 -29
  8. data/lib/lita/commands/ack.rb +15 -0
  9. data/lib/lita/commands/ack_all.rb +21 -0
  10. data/lib/lita/commands/ack_mine.rb +26 -0
  11. data/lib/lita/commands/base.rb +75 -0
  12. data/lib/lita/commands/forget.rb +13 -0
  13. data/lib/lita/commands/identify.rb +13 -0
  14. data/lib/lita/commands/incident.rb +13 -0
  15. data/lib/lita/commands/incidents_all.rb +18 -0
  16. data/lib/lita/commands/incidents_mine.rb +23 -0
  17. data/lib/lita/commands/notes.rb +15 -0
  18. data/lib/lita/commands/on_call_list.rb +13 -0
  19. data/lib/lita/commands/on_call_lookup.rb +41 -0
  20. data/lib/lita/commands/pager_me.rb +48 -0
  21. data/lib/lita/commands/resolve.rb +15 -0
  22. data/lib/lita/commands/resolve_all.rb +21 -0
  23. data/lib/lita/commands/resolve_mine.rb +26 -0
  24. data/lib/lita/handlers/commands.yml +30 -0
  25. data/lib/lita/handlers/pagerduty.rb +57 -0
  26. data/lib/pagerduty.rb +94 -0
  27. data/lib/store.rb +20 -0
  28. data/lita-pagerduty.gemspec +1 -2
  29. data/locales/en.yml +2 -2
  30. data/spec/lita/handlers/ack_all_spec.rb +26 -0
  31. data/spec/lita/handlers/ack_id_spec.rb +23 -0
  32. data/spec/lita/handlers/ack_mine_spec.rb +38 -0
  33. data/spec/lita/handlers/forget_spec.rb +22 -0
  34. data/spec/lita/handlers/identify_email_spec.rb +22 -0
  35. data/spec/lita/handlers/incident_id_spec.rb +23 -0
  36. data/spec/lita/handlers/incidents_all_spec.rb +31 -0
  37. data/spec/lita/handlers/incidents_mine_spec.rb +35 -0
  38. data/spec/lita/handlers/notes_spec.rb +29 -0
  39. data/spec/lita/handlers/on_call_list_spec.rb +21 -0
  40. data/spec/lita/handlers/on_call_lookup_spec.rb +29 -0
  41. data/spec/lita/handlers/pager_me_spec.rb +56 -0
  42. data/spec/lita/handlers/resolve_all_spec.rb +26 -0
  43. data/spec/lita/handlers/resolve_id_spec.rb +21 -0
  44. data/spec/lita/handlers/resolve_mine_spec.rb +38 -0
  45. data/spec/pagerduty_spec.rb +106 -0
  46. data/spec/spec_helper.rb +3 -189
  47. data/templates/.gitkeep +0 -0
  48. metadata +57 -35
  49. data/lib/lita/handlers/pagerduty_ack.rb +0 -77
  50. data/lib/lita/handlers/pagerduty_incident.rb +0 -68
  51. data/lib/lita/handlers/pagerduty_note.rb +0 -50
  52. data/lib/lita/handlers/pagerduty_resolve.rb +0 -77
  53. data/lib/lita/handlers/pagerduty_utility.rb +0 -136
  54. data/lib/pagerduty_helper/incident.rb +0 -64
  55. data/lib/pagerduty_helper/regex.rb +0 -8
  56. data/lib/pagerduty_helper/utility.rb +0 -43
  57. data/spec/lita/handlers/pagerduty_ack_spec.rb +0 -95
  58. data/spec/lita/handlers/pagerduty_incident_spec.rb +0 -103
  59. data/spec/lita/handlers/pagerduty_note_spec.rb +0 -43
  60. data/spec/lita/handlers/pagerduty_resolve_spec.rb +0 -95
  61. data/spec/lita/handlers/pagerduty_utility_spec.rb +0 -59
@@ -0,0 +1,48 @@
1
+ module Commands
2
+ class PagerMe
3
+ include Base
4
+
5
+ def call
6
+ response message: 'pager_me.success', params: success_response_params
7
+ rescue Exceptions::SchedulesEmptyList
8
+ response schedules_empty_list
9
+ rescue Exceptions::UserNotIdentified
10
+ response message: 'identify.missing'
11
+ rescue Exceptions::UsersEmptyList
12
+ response message: 'identify.unrecognised'
13
+ rescue Exceptions::OverrideUnsuccess
14
+ response message: 'pager_me.failure'
15
+ end
16
+
17
+ private
18
+
19
+ def schedules_empty_list
20
+ {
21
+ message: 'on_call_lookup.no_matching_schedule',
22
+ params: {
23
+ schedule_name: message.match_data[1].strip
24
+ }
25
+ }
26
+ end
27
+
28
+ def schedule
29
+ @schedule ||= pagerduty.get_schedules(
30
+ query: message.match_data[1].strip
31
+ ).first
32
+ end
33
+
34
+ def override
35
+ @override ||= pagerduty.override(
36
+ schedule[:id], current_user[:id], message.match_data[2].strip.to_i
37
+ )
38
+ end
39
+
40
+ def success_response_params
41
+ {
42
+ name: override[:user][:summary],
43
+ email: current_user[:email],
44
+ finish: override[:end]
45
+ }
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,15 @@
1
+ module Commands
2
+ class Resolve
3
+ include Base
4
+
5
+ def call
6
+ incident_id = message.match_data['incident_id']
7
+ return if incident_id =~ /\A(all|mine)\z/i
8
+
9
+ pagerduty.manage_incidents(:resolve, [incident_id])
10
+ response message: 'all.resolved', params: { list: incident_id.to_s }
11
+ rescue Exceptions::IncidentManageUnsuccess
12
+ nil
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,21 @@
1
+ module Commands
2
+ class ResolveAll
3
+ include Base
4
+
5
+ def call
6
+ ids = pagerduty.get_incidents(query_params).map { |i| i[:id] }
7
+ pagerduty.manage_incidents(:resolve, ids)
8
+ response message: 'all.resolved', params: { list: ids.join(', ') }
9
+ rescue Exceptions::IncidentsEmptyList
10
+ response message: 'incident.none'
11
+ rescue Exceptions::IncidentManageUnsuccess
12
+ nil
13
+ end
14
+
15
+ private
16
+
17
+ def query_params
18
+ { statuses: %w[triggered acknowledged] }
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,26 @@
1
+ module Commands
2
+ class ResolveMine
3
+ include Base
4
+
5
+ def call
6
+ ids = pagerduty.get_incidents(query_params).map { |i| i[:id] }
7
+ pagerduty.manage_incidents(:resolve, ids)
8
+ response message: 'all.resolved', params: { list: ids.join(', ') }
9
+ rescue Exceptions::UserNotIdentified
10
+ response message: 'incident.none_mine'
11
+ rescue Exceptions::IncidentsEmptyList
12
+ response message: 'incident.none_mine'
13
+ rescue Exceptions::IncidentManageUnsuccess
14
+ nil
15
+ end
16
+
17
+ private
18
+
19
+ def query_params
20
+ {
21
+ statuses: %w[triggered acknowledged],
22
+ 'user_ids[]' => current_user[:id]
23
+ }
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,30 @@
1
+ - pattern: ^pager\sidentify\s(?<email>[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+)$
2
+ method: identify
3
+ - pattern: ^pager\sforget$
4
+ method: forget
5
+ - pattern: ^pager\sincidents\sall$
6
+ method: incidents_all
7
+ - pattern: ^pager\sincidents\smine$
8
+ method: incidents_mine
9
+ - pattern: ^pager\sincident\s(?<incident_id>[a-zA-Z0-9+]+)$
10
+ method: incident
11
+ - pattern: ^pager\snotes\s(?<incident_id>[a-zA-Z0-9+]+)$
12
+ method: notes
13
+ - pattern: ^pager\sack\sall$
14
+ method: ack_all
15
+ - pattern: ^pager\sack\smine$
16
+ method: ack_mine
17
+ - pattern: ^pager\sack\s(?<incident_id>[a-zA-Z0-9+]+)$
18
+ method: ack
19
+ - pattern: ^pager\sresolve\sall$
20
+ method: resolve_all
21
+ - pattern: ^pager\sresolve\smine$
22
+ method: resolve_mine
23
+ - pattern: ^pager\sresolve\s(?<incident_id>[a-zA-Z0-9+]+)$
24
+ method: resolve
25
+ - pattern: ^pager\soncall$
26
+ method: on_call_list
27
+ - pattern: ^pager\soncall\s(.*)$
28
+ method: on_call_lookup
29
+ - pattern: ^pager\s+me\s+(.+?)\s+(\d+)m?$
30
+ method: pager_me
@@ -0,0 +1,57 @@
1
+ module Lita
2
+ module Handlers
3
+ class Pagerduty < Handler
4
+ namespace 'Pagerduty'
5
+ config :api_key, required: true
6
+ config :email, required: true
7
+
8
+ COMMANDS_PATH = File.read("#{File.dirname(__FILE__)}/commands.yml")
9
+ COMMANDS = YAML.safe_load(COMMANDS_PATH)
10
+ COMMANDS.each do |command|
11
+ route(
12
+ /#{command['pattern']}/,
13
+ command['method'].to_sym,
14
+ command: true,
15
+ help: {
16
+ "help.#{command['method']}.syntax" =>
17
+ "help.#{command['method']}.desc"
18
+ }
19
+ )
20
+ end
21
+
22
+ def method_missing(method, message)
23
+ super if COMMANDS.map { |i| i['method'] }.include? method
24
+ response = Object.const_get(
25
+ 'Commands::' << method.to_s.split('_').map(&:capitalize).join
26
+ ).send(:call, message, pagerduty, store)
27
+ handle_response(message, response) if response
28
+ end
29
+
30
+ def respond_to_missing?(method, include_private = false)
31
+ COMMANDS.map { |i| i['method'] }.include?(method) || super
32
+ end
33
+
34
+ def pagerduty
35
+ @pagerduty ||= ::Pagerduty.new(http, config.api_key, config.email)
36
+ end
37
+
38
+ def store
39
+ @store ||= Store.new(redis)
40
+ end
41
+
42
+ def handle_response(message, response)
43
+ message.reply case response
44
+ when String
45
+ response
46
+ when Hash
47
+ t response[:message], response[:params]
48
+ when Array
49
+ response.map { |item| t item[:message], item[:params] }
50
+ .join("\n")
51
+ end
52
+ end
53
+
54
+ Lita.register_handler(self)
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,94 @@
1
+ class Pagerduty
2
+ def initialize(http, token, email)
3
+ @token = token
4
+ @http = http
5
+ http.url_prefix = 'https://api.pagerduty.com/'
6
+ http.headers = headers(email)
7
+ end
8
+
9
+ def get_incidents(params = {})
10
+ response = @http.get '/incidents', params
11
+ data = JSON.parse(response.body, symbolize_names: true)
12
+ .fetch(:incidents, [])
13
+ raise Exceptions::IncidentsEmptyList if data.empty?
14
+
15
+ data
16
+ end
17
+
18
+ def get_users(params = {})
19
+ response = @http.get '/users', params
20
+ data = JSON.parse(response.body, symbolize_names: true).fetch(:users, [])
21
+ raise Exceptions::UsersEmptyList if data.empty?
22
+
23
+ data
24
+ end
25
+
26
+ def get_schedules(params = {})
27
+ response = @http.get '/schedules', params
28
+ data = JSON.parse(response.body, symbolize_names: true)
29
+ .fetch(:schedules, [])
30
+ raise Exceptions::SchedulesEmptyList if data.empty?
31
+
32
+ data
33
+ end
34
+
35
+ def get_oncall_user(params = {})
36
+ response = @http.get '/oncalls', params
37
+ data = JSON.parse(response.body, symbolize_names: true).fetch(:oncalls, [])
38
+ raise Exceptions::NoOncallUser if data.empty?
39
+
40
+ data.first.fetch(:user)
41
+ end
42
+
43
+ def get_incident(id = '404stub')
44
+ response = @http.get "/incidents/#{id}"
45
+ raise Exceptions::IncidentNotFound if response.status == 404
46
+
47
+ JSON.parse(response.body, symbolize_names: true).fetch(:incident, nil)
48
+ end
49
+
50
+ def get_notes_by_incident_id(incident_id)
51
+ response = @http.get "/incidents/#{incident_id}/notes"
52
+ raise Exceptions::IncidentNotFound if response.status == 404
53
+
54
+ data = JSON.parse(response.body, symbolize_names: true).fetch(:notes, [])
55
+ raise Exceptions::NotesEmptyList if data.empty?
56
+
57
+ data
58
+ end
59
+
60
+ def manage_incidents(action, ids)
61
+ incidents = ids.map do |id|
62
+ { id: id, type: 'incident_reference', status: "#{action}d" }
63
+ end
64
+ payload = { incidents: incidents }
65
+ response = @http.put '/incidents', payload
66
+ raise Exceptions::IncidentManageUnsuccess if response.status != 200
67
+
68
+ response
69
+ end
70
+
71
+ def override(schedule_id, user_id, minutes)
72
+ from = ::Time.now.utc + 10
73
+ to = from + (60 * minutes)
74
+ payload = { override: {
75
+ start: from.iso8601,
76
+ end: to.iso8601,
77
+ user: { id: user_id, type: 'user_reference' }
78
+ } }
79
+ response = @http.post "/schedules/#{schedule_id}/overrides", payload
80
+ raise Exceptions::OverrideUnsuccess if response.status != 201
81
+
82
+ JSON.parse(response.body, symbolize_names: true).fetch(:override, nil)
83
+ end
84
+
85
+ private
86
+
87
+ def headers(email)
88
+ {
89
+ 'Accept' => 'application/vnd.pagerduty+json;version=2',
90
+ 'Authorization' => "Token token=#{@token}",
91
+ 'From' => email
92
+ }
93
+ end
94
+ end
@@ -0,0 +1,20 @@
1
+ class Store
2
+ def initialize(redis)
3
+ @redis = redis
4
+ end
5
+
6
+ def get_user(response)
7
+ email = @redis.get("user_#{response.user.id}")
8
+ raise Exceptions::UserNotIdentified unless email
9
+
10
+ email
11
+ end
12
+
13
+ def remember_user(response)
14
+ @redis.set("user_#{response.user.id}", response.match_data['email'])
15
+ end
16
+
17
+ def forget_user(response)
18
+ @redis.del("user_#{response.user.id}")
19
+ end
20
+ end
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = 'lita-pagerduty'
3
- spec.version = '0.2.0'
3
+ spec.version = '1.0.0'
4
4
  spec.authors = ['Eric Sigler']
5
5
  spec.email = ['eric@pagerduty.com']
6
6
  spec.description = 'A Lita handler to interact with PagerDuty'
@@ -15,7 +15,6 @@ Gem::Specification.new do |spec|
15
15
  spec.require_paths = ['lib']
16
16
 
17
17
  spec.add_runtime_dependency 'lita', '>= 4.0'
18
- spec.add_runtime_dependency 'pagerduty-sdk', '>= 1.0.9'
19
18
 
20
19
  spec.add_development_dependency 'bundler', '~> 1.3'
21
20
  spec.add_development_dependency 'coveralls'
@@ -78,13 +78,13 @@ en:
78
78
  acknowledged: "Acknowledged: %{list}"
79
79
  resolved: "Resolved: %{list}"
80
80
  note:
81
- show: "%{id}: %{content} (%{email})"
81
+ show: "%{id}: %{content} (%{user})"
82
82
  on_call_list:
83
83
  response: "Available schedules:\n%{schedules}"
84
84
  no_schedules_found: "No schedules found"
85
85
  on_call_lookup:
86
86
  response: "%{name} (%{email}) is currently on call for %{schedule_name}"
87
- no_matching_schedule: "No matching schedules found for '%{schedule_name}'"
87
+ no_matching_schedule: "No matching schedules found for '%{schedule_name}'"
88
88
  no_one_on_call: "No one is currently on call for %{schedule_name}"
89
89
  pager_me:
90
90
  success: "%{name} (%{email}) is now on call until %{finish}"
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe Lita::Handlers::Pagerduty, lita_handler: true do
4
+ context 'ack all' do
5
+ it do
6
+ is_expected.to route_command('pager ack all').to(:ack_all)
7
+ end
8
+
9
+ it 'empty list' do
10
+ expect_any_instance_of(Pagerduty).to receive(:get_incidents).and_raise(Exceptions::IncidentsEmptyList)
11
+ send_command('pager ack all')
12
+ expect(replies.last).to eq('No triggered, open, or acknowledged incidents')
13
+ end
14
+
15
+ it 'list of incidents' do
16
+ expect_any_instance_of(Pagerduty).to receive(:get_incidents).and_return([
17
+ { id: 'ABC123' }, { id: 'ABC124' }
18
+ ])
19
+ expect_any_instance_of(Pagerduty).to receive(:manage_incidents).and_return(
20
+ OpenStruct.new(status: 200)
21
+ )
22
+ send_command('pager ack all')
23
+ expect(replies.last).to eq('Acknowledged: ABC123, ABC124')
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe Lita::Handlers::Pagerduty, lita_handler: true do
4
+ context 'ack ABC123' do
5
+ it do
6
+ is_expected.to route_command('pager ack ABC123').to(:ack)
7
+ end
8
+
9
+ it 'not found' do
10
+ expect_any_instance_of(Pagerduty).to receive(:manage_incidents).and_raise(Exceptions::IncidentManageUnsuccess)
11
+ send_command('pager ack ABC123')
12
+ expect(replies.last).to be_nil
13
+ end
14
+
15
+ it 'found' do
16
+ expect_any_instance_of(Pagerduty).to receive(:manage_incidents).and_return(
17
+ OpenStruct.new(status: 200)
18
+ )
19
+ send_command('pager ack ABC123')
20
+ expect(replies.last).to eq 'Acknowledged: ABC123'
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe Lita::Handlers::Pagerduty, lita_handler: true do
4
+ context 'ack mine' do
5
+ it do
6
+ is_expected.to route_command('pager ack mine').to(:ack_mine)
7
+ end
8
+
9
+ it 'unknown user' do
10
+ user = Lita::User.create(123, name: 'foo')
11
+ send_command('pager ack mine', as: user)
12
+ expect(replies.last).to eq('You have no triggered, open, or acknowledged incidents')
13
+ end
14
+
15
+ it 'empty list' do
16
+ user = Lita::User.create(123, name: 'foo')
17
+ send_command('pager identify foo@pagerduty.com', as: user)
18
+ expect_any_instance_of(Pagerduty).to receive(:get_users).and_return([{ id: 'abc123' }])
19
+ expect_any_instance_of(Pagerduty).to receive(:get_incidents).and_raise(Exceptions::IncidentsEmptyList)
20
+ send_command('pager ack mine', as: user)
21
+ expect(replies.last).to eq('You have no triggered, open, or acknowledged incidents')
22
+ end
23
+
24
+ it 'list of incidents' do
25
+ user = Lita::User.create(123, name: 'foo')
26
+ send_command('pager identify foo@pagerduty.com', as: user)
27
+ expect_any_instance_of(Pagerduty).to receive(:get_users).and_return([{ id: 'abc123' }])
28
+ expect_any_instance_of(Pagerduty).to receive(:get_incidents).and_return([
29
+ { id: 'ABC123' }, { id: 'ABC124' }
30
+ ])
31
+ expect_any_instance_of(Pagerduty).to receive(:manage_incidents).and_return(
32
+ OpenStruct.new(status: 200)
33
+ )
34
+ send_command('pager ack mine', as: user)
35
+ expect(replies.last).to eq('Acknowledged: ABC123, ABC124')
36
+ end
37
+ end
38
+ end