lita-pagerduty 0.2.0 → 1.0.0

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