lita-interrupt 0.1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b15811f72a7f29073b61d5f8bcdcd7cea23e7ed6
4
+ data.tar.gz: d67e7c6d51bf17ebd58946b48e94e8c02ab21d34
5
+ SHA512:
6
+ metadata.gz: fb320ff975a01d83543a859c2c906c470f6b8f8f577e3896213b948ab5e04b714b09f8aefa3d654790da6f87dc72bff3b14c95e12e836f869bea9f94b28c2910
7
+ data.tar.gz: b265df0952d50b4423fb09eeec78de07b6c3c2f834440ee9f0b37bfb9d920fd646a7ac77cab6bd29548a9b839e05165e6796534ddbf8186cc90d0205d1d2ff66
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ vendor
19
+ /alita
20
+
21
+ .*.sw[op]
22
+ ~*
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ script: bundle exec rake
5
+ before_install:
6
+ - gem update --system
7
+ services:
8
+ - redis-server
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ Copyright (c) 2018 Noel R. Cower. All rights reserved.
2
+
3
+ Redistribution and use in source and binary forms, with or without
4
+ modification, are permitted provided that the following conditions are
5
+ met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright
8
+ notice, this list of conditions and the following disclaimer.
9
+ 2. Redistributions in binary form must reproduce the above copyright
10
+ notice, this list of conditions and the following disclaimer in the
11
+ documentation and/or other materials provided with the distribution.
12
+ 3. Neither the name of the copyright holder nor the names of its
13
+ contributors may be used to endorse or promote products derived from
14
+ this software without specific prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
17
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
22
+ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,91 @@
1
+ # lita-interrupt
2
+
3
+ [![Build Status](https://travis-ci.org/nilium/lita-interrupt.png?branch=master)](https://travis-ci.org/nilium/lita-interrupt)
4
+ [![Coverage Status](https://coveralls.io/repos/nilium/lita-interrupt/badge.png)](https://coveralls.io/r/nilium/lita-interrupt)
5
+
6
+ lita-interrupt is a plugin that enables paging of individual services in
7
+ PagerDuty using an alias. It is primarily intended for use in on-call situations
8
+ and is geared towards use in Slack (hasn't been tested on other chat platforms.)
9
+
10
+ ## Installation
11
+
12
+ Add lita-interrupt to your Lita instance's Gemfile:
13
+
14
+ ``` ruby
15
+ gem "lita-interrupt", :git => 'https://github.com/nilium/lita-interrupt', :branch => 'master'
16
+ ```
17
+
18
+ Currently, there are no published releases, so you have to use the Git
19
+ repository.
20
+
21
+ ## Configuration
22
+
23
+ lita-interrupt you to set `config.handlers.interrupt.pagerduty_teams` to a hash
24
+ of team names to PagerDuty API tokens. For example:
25
+
26
+ ```ruby
27
+ Lita.configure do |config|
28
+
29
+ config.handlers.interrupt.pagerduty_teams = {
30
+ 'team' => 'API-TOKEN',
31
+ }
32
+
33
+ end
34
+ ```
35
+
36
+ For now, team names must be a lowercase string.
37
+
38
+ Users in the `pagerduty_admins` auth group can list services, teams, and create
39
+ and remove aliases.
40
+
41
+ To override the name required for PagerDuty integrations, you can set the
42
+ `config.handlers.interrupt.integration_name` config value. By default, it is set
43
+ to `lita-interrupt`.
44
+
45
+ ### PagerDuty Integrations
46
+
47
+ lita-interrupt looks for Events v2 API integrations under PagerDuty services
48
+ with the configured integration name (default: `lita-interrupt`). In order to be
49
+ able to create an alias for a service, you must first add an integration with
50
+ the required name to the service you want to create a notification for.
51
+
52
+ ## Usage
53
+
54
+ ### `call ALIAS [MESSAGE]`
55
+
56
+ Send a PagerDuty notification to the service identified by the alias. Optionally
57
+ includes a message with the alert.
58
+
59
+ All alerts attempt to include the caller's name and the channel the notification
60
+ was sent from.
61
+
62
+ ### `int service ls [TEAM [QUERY]]`
63
+
64
+ List all service IDs configured with a lita-interrupt integration. You can use
65
+ the IDs returned by this to create an alias for a particular team.
66
+
67
+ Requires `pagerduty_admins` auth.
68
+
69
+ ### `int alias new NAME TEAM SERVICE_ID`
70
+
71
+ Create an alias with the given name for the team and service ID. Once created,
72
+ the alias can be used to send a PagerDuty alert to the service.
73
+
74
+ Requires `pagerduty_admins` auth.
75
+
76
+ ### `int alias ls` or `who can I call?`
77
+
78
+ (Also accepts `who can I call` minus the question mark -- matching is
79
+ case-insensitive.)
80
+
81
+ List call-able aliases.
82
+
83
+ ### `int alias rm NAME`
84
+
85
+ Remove an alias with the given name.
86
+
87
+ Requires `pagerduty_admins` auth.
88
+
89
+ ### `int team ls`
90
+
91
+ List team IDs.
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -0,0 +1,36 @@
1
+ require 'faraday'
2
+ require 'faraday_middleware'
3
+ require 'pager_duty/connection'
4
+
5
+ module InterruptHelper
6
+ module PagerDuty
7
+ def pagerduty(token)
8
+ @pagerduty_clients ||= Hash.new { |h, k|
9
+ h[k] = ::PagerDuty::Connection.new(k)
10
+ }
11
+ @pagerduty_clients[token]
12
+ end
13
+
14
+ # @return [::Faraday]
15
+ def events_v2
16
+ @events_v2_client ||= Faraday.new do |conn|
17
+ conn.url_prefix = "https://events.pagerduty.com/v2/"
18
+ conn.use(RaiseNon202)
19
+ conn.request(:json)
20
+ conn.response(:json)
21
+ conn.headers[:accept] = "application/json"
22
+ conn.adapter(Faraday.default_adapter)
23
+ end
24
+ end
25
+
26
+ class APIError < RuntimeError; end
27
+
28
+ class RaiseNon202 < Faraday::Middleware
29
+ def call(env)
30
+ response = @app.call env
31
+ raise APIError, "bad response code: #{response.status}" unless response.status == 202
32
+ response
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,12 @@
1
+ require "lita"
2
+
3
+ Lita.load_locales Dir[File.expand_path(
4
+ File.join("..", "..", "locales", "*.yml"), __FILE__
5
+ )]
6
+
7
+ require "lita/handlers/interrupt"
8
+
9
+ Lita::Handlers::Interrupt.template_root File.expand_path(
10
+ File.join("..", "..", "templates"),
11
+ __FILE__
12
+ )
@@ -0,0 +1,309 @@
1
+ require 'json'
2
+ require 'interrupt_helper/pager_duty'
3
+
4
+ module Lita
5
+ module Handlers
6
+ class Interrupt < Handler
7
+ config :pagerduty_teams, required: true, type: Hash # { lowercase('team') => 'token' }
8
+ config :integration_name, type: String, default: 'lita-interrupt'.freeze
9
+
10
+ # Keys in Redis
11
+ CALL_ALIASES = 'call_alias'.freeze
12
+
13
+ # Constants used by the PagerDuty API
14
+ EVENTS_API_V2_INBOUND = 'events_api_v2_inbound_integration'.freeze
15
+
16
+ include ::InterruptHelper::PagerDuty
17
+
18
+ ## ROUTES
19
+
20
+ route(
21
+ /^call\s+(?<alias_id>[^\s]+)(?:\s+(?<message>.+))?\s*$/i,
22
+ :call_alias,
23
+ command: true,
24
+ help: { t('help.call.syntax') => t('help.call.desc') }
25
+ )
26
+
27
+ route(
28
+ /^int\s+team\s+ls\s*$/i,
29
+ :list_teams,
30
+ restrict_to: %w[pagerduty_admins],
31
+ command: true,
32
+ help: { t('help.team_ls.syntax') => t('help.team_ls.desc') }
33
+ )
34
+
35
+ route(
36
+ /^int\s+alias\s+new\s+(?<alias_id>\S+)\s+(?<team_id>\S+)\s+(?<service_id>\S+)\s*$/i,
37
+ :create_alias,
38
+ command: true,
39
+ restrict_to: %w[pagerduty_admins],
40
+ help: { t('help.alias_new.syntax') => t('help.alias_new.desc') }
41
+ )
42
+
43
+ route(
44
+ /^int\s+alias\s+rm\s+(?<alias_id>\S+)\s*$/i,
45
+ :remove_alias,
46
+ command: true,
47
+ restrict_to: %w[pagerduty_admins],
48
+ help: { t('help.alias_rm.syntax') => t('help.alias_rm.desc') }
49
+ )
50
+
51
+ route(
52
+ /^(int\s+alias\s+ls|who\s+can\s+i\s+call\??)\s*$/i,
53
+ :list_aliases,
54
+ command: true,
55
+ help: { t('help.alias_ls.syntax') => t('help.alias_ls.desc') }
56
+ )
57
+
58
+ route(
59
+ /^int\s+service\s+ls(\s+(?<team_id>\S+)(\s+(?<query>.+))?)?\s*$/i,
60
+ :list_services,
61
+ command: true,
62
+ restrict_to: %w[pagerduty_admins],
63
+ help: { t('help.service_ls.syntax') => t('help.service_ls.desc') }
64
+ )
65
+
66
+ ## CALLBACKS
67
+
68
+ # @param [Lita::Response] response
69
+ def call_alias(response)
70
+ alias_id = response.match_data['alias_id'].downcase
71
+
72
+ callee = get_call_alias(alias_id)
73
+ return response.reply(t('call.no_alias', name: alias_id)) unless callee
74
+
75
+ request = gen_call_request(response, callee['integration']['integration_key'])
76
+ events_v2.post('enqueue', request)
77
+ response.reply(t('call.ok', name: alias_id))
78
+
79
+ rescue APIError => ae
80
+ log_exception(__callee__, ex)
81
+ response.reply(t('call.api_error', id: alias_id, message: ae.message))
82
+ rescue Exception => ex
83
+ send_exception(response, __callee__, ex)
84
+ end
85
+
86
+ # @param [Lita::Response] response
87
+ def list_teams(response)
88
+ names = config.pagerduty_teams.keys
89
+ if names.empty?
90
+ response.reply(t('team_ls.no_teams'))
91
+ return
92
+ end
93
+ response.reply(
94
+ names.sort.map do |team_id|
95
+ t('team_ls.entry', team_id: team_id)
96
+ end.join("\n")
97
+ )
98
+ rescue Exception => ex
99
+ send_exception(response, __callee__, ex)
100
+ end
101
+
102
+ # @param [Lita::Response] response
103
+ def create_alias(response)
104
+ alias_id = response.match_data['alias_id'].downcase
105
+ team_id = response.match_data['team_id'].downcase
106
+ service_id = response.match_data['service_id']
107
+
108
+ pd = team(team_id)
109
+ if pd.nil?
110
+ response.reply(t('alias_new.no_team', team_id: team_id))
111
+ return
112
+ end
113
+
114
+ integration = get_events_api_integration(pd, service_id)
115
+ unless integration
116
+ return response.reply(t('alias_new.no_integration', service_id: service_id, integration_name: config.integration_name))
117
+ end
118
+
119
+ set_call_alias(alias_id, team_id, service_id, integration)
120
+ response.reply(t('alias_new.ok', id: alias_id))
121
+
122
+ rescue Exception => ex
123
+ send_exception(response, __callee__, ex)
124
+ end
125
+
126
+ # @param [Lita::Response] response
127
+ def list_aliases(response)
128
+ aliases = redis.hgetall(CALL_ALIASES)
129
+ if aliases.empty?
130
+ response.reply(t('alias_ls.no_aliases'))
131
+ return
132
+ end
133
+
134
+ response.reply(
135
+ aliases.keys.sort.map do |alias_id|
136
+ obj = MultiJson.load(aliases[alias_id])
137
+ t(
138
+ 'alias_ls.entry',
139
+ alias_id: alias_id,
140
+ service_name: obj['integration']['service']['summary'],
141
+ team_id: obj['team_id']
142
+ )
143
+ end.join("\n")
144
+ )
145
+
146
+ rescue Exception => ex
147
+ send_exception(response, __callee__, ex)
148
+ end
149
+
150
+ # @param [Lita::Response] response
151
+ def remove_alias(response)
152
+ alias_id = response.match_data['alias_id'].downcase
153
+ if redis.hdel(CALL_ALIASES, alias_id) > 0 then
154
+ response.reply(t('alias_rm.ok', id: alias_id))
155
+ else
156
+ response.reply(t('alias_rm.no_alias', id: alias_id))
157
+ end
158
+
159
+ rescue Exception => ex
160
+ send_exception(response, __callee__, ex)
161
+ end
162
+
163
+ # @param [Lita::Response] response
164
+ def list_services(response)
165
+ team_id = response.match_data['team_id']
166
+ unless team_id.nil? || team_id.empty?
167
+ response.reply(list_team_services(team_id, response).join("\n"))
168
+ return
169
+ end
170
+
171
+ response.reply(
172
+ config.pagerduty_teams.keys.sort.flat_map do |team_id|
173
+ [
174
+ t('service_ls.team', team_id: team_id),
175
+ *list_team_services(team_id, response)
176
+ ]
177
+ end.join("\n")
178
+ )
179
+
180
+ rescue APIError => ae
181
+ log_exception(__callee__, ex)
182
+ response.reply(t('call.api_error', id: alias_id, message: ae.message))
183
+ rescue Exception => ex
184
+ send_exception(response, __callee__, ex)
185
+ end
186
+
187
+ def list_team_services(team_id, response)
188
+ pd = team(team_id)
189
+ if pd.nil?
190
+ response.reply(t('service_ls.no_team', team_id: team_id))
191
+ return
192
+ end
193
+
194
+ query = (response.match_data['query'] || '').strip
195
+ params = {
196
+ query: query || '',
197
+ include: %w[integrations]
198
+ }
199
+
200
+ results = pd.get('services', query_params: params)
201
+
202
+ # Only list Events API 2-integrated services with suitable integrations
203
+ services = (results['services'] || []).select do |svc|
204
+ (svc['integrations'] || []).any? { |i| is_suitable_integration?(i) }
205
+ end
206
+ return response.reply(t('service_ls.no_services')) if services.empty?
207
+
208
+ services.sort_by { |svc| svc['name'] }.map { |svc|
209
+ t('service_ls.entry', service_name: svc['name'], service_id: svc['id'])
210
+ }
211
+ end
212
+
213
+ ## IMPLEMENTATION
214
+
215
+ def team(team_id)
216
+ pagerduty(config.pagerduty_teams[team_id])
217
+ end
218
+
219
+ def send_exception(response, callee, ex)
220
+ log_exception(callee, ex)
221
+ response.reply(t('exception', handler: callee, class: ex.class.name, message: ex.message))
222
+ end
223
+
224
+ def log_exception(callee, ex)
225
+ log.warn("Exception occurred #{ex.class}: #{ex.message}:\n#{ex.backtrace.join("\n")}")
226
+ end
227
+
228
+ def get_room_name(response)
229
+ room = Lita::Room.find_by_id(response.message.source.room_object.id)
230
+ room.name || room.mention_name if room
231
+ end
232
+
233
+ def gen_call_request(response, routing_key)
234
+ # Build summary message sent in the event
235
+ room_name = get_room_name(response) || ''
236
+ room_note = room_name.empty? ? '' : " in ##{room_name}"
237
+ msg = "You've been called by #{response.user.name}#{room_note}."
238
+
239
+ sub_msg = (response.match_data['message'] || '').strip
240
+ unless sub_msg.empty?
241
+ sub_msg = "#{sub_msg}." unless /\p{Terminal_Punctuation}["']?$/ =~ sub_msg
242
+ msg = "#{msg}\n#{sub_msg}"
243
+ end
244
+
245
+ source = 'Slack'
246
+ source = "##{room_name}" unless room_name.empty?
247
+
248
+ {
249
+ routing_key: routing_key,
250
+ event_action: 'trigger',
251
+ payload: {
252
+ summary: msg,
253
+ source: source,
254
+ severity: 'critical',
255
+ component: 'Human',
256
+ group: 'On-Call',
257
+ class: 'Notification'
258
+ }
259
+ }
260
+ end
261
+
262
+ # @return [Hash] A hash describing the policy alias. If no policy alias is defined, returns nil.
263
+ def get_call_alias(alias_id)
264
+ callee_json = redis.hget(CALL_ALIASES, alias_id)
265
+ return nil if callee_json.nil?
266
+ MultiJson.load(callee_json)
267
+ end
268
+
269
+ def set_call_alias(alias_id, team_id, service_id, integration)
270
+ obj = {
271
+ version: 1,
272
+ team_id: team_id.to_s,
273
+ alias_id: alias_id.to_s,
274
+ service_id: service_id,
275
+ integration: integration
276
+ }
277
+ redis.hset(CALL_ALIASES, alias_id.downcase, MultiJson.dump(obj))
278
+ end
279
+
280
+ def get_events_api_integration(pd, service_id)
281
+ service = pd.get(
282
+ "services/#{service_id}",
283
+ query_params: { include: %w[integrations] }
284
+ )&.fetch('service')
285
+ return nil unless service
286
+ index = service['integrations'].index do |int|
287
+ int['type'] == EVENTS_API_V2_INBOUND &&
288
+ int['name'].downcase.gsub(/[_.\s]/, '-') == config.integration_name
289
+ end
290
+ index && service['integrations'][index]
291
+ end
292
+
293
+ def required_integration_name
294
+ @integration_name ||= normalize_name(config.integration_name)
295
+ end
296
+
297
+ def normalize_name(name)
298
+ name.downcase.gsub(/[\s\._]/, '-')
299
+ end
300
+
301
+ def is_suitable_integration?(integration)
302
+ integration['type'] == EVENTS_API_V2_INBOUND &&
303
+ normalize_name(integration['name']) == required_integration_name
304
+ end
305
+
306
+ Lita.register_handler(self)
307
+ end
308
+ end
309
+ end
@@ -0,0 +1,30 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = 'lita-interrupt'
3
+ spec.version = '0.1.0'
4
+ spec.authors = ['Noel Cower']
5
+ spec.email = ['ncower@gmail.com']
6
+ spec.description = 'lita-interrupt interrupts a user by assigning them to a new incident in PagerDuty'
7
+ spec.summary = 'Interrupts people through the power of PagerDuty'
8
+ spec.homepage = 'https://github.com/nilium/lita-interrupt'
9
+ spec.license = 'BSD-3-Clause'
10
+ spec.metadata = { 'lita_plugin_type' => 'handler' }
11
+
12
+ spec.files = `git ls-files`.split($/)
13
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
14
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
15
+ spec.require_paths = ['lib']
16
+
17
+ spec.required_ruby_version = '~> 2.3'
18
+
19
+ spec.add_runtime_dependency 'lita', '>= 4.7'
20
+ spec.add_runtime_dependency 'pager_duty-connection', '~> 1.0'
21
+
22
+ spec.add_development_dependency 'bundler', '~> 1.3'
23
+ spec.add_development_dependency 'coveralls'
24
+ spec.add_development_dependency 'pry-byebug'
25
+ spec.add_development_dependency 'rack-test'
26
+ spec.add_development_dependency 'rake'
27
+ spec.add_development_dependency 'rspec', '>= 3.0.0'
28
+ spec.add_development_dependency 'rubocop'
29
+ spec.add_development_dependency 'simplecov'
30
+ end
@@ -0,0 +1,51 @@
1
+ en:
2
+ lita:
3
+ handlers:
4
+ interrupt:
5
+ team_ls:
6
+ no_teams: "No teams are configured."
7
+ entry: "• `%{team_id}`"
8
+ service_ls:
9
+ no_services: ":failed: No services found."
10
+ no_team: ":failed: Team %{team_id} not found."
11
+ api_error: ":failed: Unable to look up services: %{message}"
12
+ entry: "• `%{service_id}` - _%{service_name}_"
13
+ team: "*%{team_id}*"
14
+ alias_new:
15
+ no_integration: ":failed: Service `%{service_id}` does not exist or does not have an Events v2 integration named `%{integration_name}`."
16
+ no_team: ":failed: Team %{team_id} not found."
17
+ ok: ":success: Alias %{id} created."
18
+ alias_rm:
19
+ no_alias: ":failed: Alias %{id} not found."
20
+ ok: ":success: Alias %{id} deleted."
21
+ alias_ls:
22
+ entry: "• `%{alias_id}` → %{service_name} (%{team_id})"
23
+ no_aliases: "No aliases defined."
24
+ call:
25
+ no_alias: ":failed: No alias found with the name %{name}"
26
+ api_error: ":failed: Unable to call %{id}: %{message}"
27
+ ok: ":notify: %{name} notified."
28
+
29
+ exception: |-
30
+ :failed: Exception in handler %{handler} (%{class}):
31
+ %{message}
32
+
33
+ help:
34
+ call:
35
+ syntax: 'call <NAME> [MESSAGE]'
36
+ desc: 'Notify the recipient with an optional message via PagerDuty.'
37
+ team_ls:
38
+ syntax: 'int team ls'
39
+ desc: 'List team names.'
40
+ alias_new:
41
+ syntax: 'int alias new <NAME> <TEAM> <SERVICE-ID>'
42
+ desc: 'Create a service call alias (requires the service to have a correctly-named Events v2 integration).'
43
+ alias_ls:
44
+ syntax: 'int alias ls | who can i call'
45
+ desc: 'List aliases.'
46
+ alias_rm:
47
+ syntax: 'int alias rm <NAME>'
48
+ desc: 'Delete an existing service alias.'
49
+ service_ls:
50
+ syntax: 'int service ls [TEAM [QUERY]]'
51
+ desc: 'List services, optionally filtering them by team and with a query (by title).'
@@ -0,0 +1,143 @@
1
+ require "spec_helper"
2
+
3
+ describe Lita::Handlers::Interrupt, lita_handler: true do
4
+
5
+ routes = {
6
+ # :method => ["MESSAGE", ...]
7
+
8
+ :call_alias => [
9
+ 'call dev the tirefire seems unhappy today',
10
+ 'call ops we forgot to add more tires',
11
+ 'call dev ',
12
+ 'call ops ',
13
+ 'call dev',
14
+ 'call ops'
15
+ ],
16
+
17
+ :list_teams => [
18
+ 'int team ls ',
19
+ 'int team ls'
20
+ ],
21
+
22
+ :create_alias => [
23
+ 'int alias new alias1 team1 serviceid',
24
+ 'int alias new alias1 team1 serviceid'
25
+ ],
26
+
27
+ :remove_alias => [
28
+ 'int alias rm alias1'
29
+ ],
30
+
31
+ :list_aliases => [
32
+ 'int alias ls ',
33
+ 'int alias ls',
34
+ 'who can I call?',
35
+ 'who can i call?',
36
+ 'WHO CAN i CALL ' # does it pass the Scott check?
37
+ ],
38
+
39
+ :list_services => [
40
+ 'int service ls',
41
+ 'int service ls ',
42
+ 'int service ls team1',
43
+ 'int service ls team1 query text'
44
+ ]
45
+
46
+ }
47
+
48
+ describe "Basic user access" do
49
+ %i[
50
+ list_aliases
51
+ call_alias
52
+ ].each do |method_name|
53
+ it "routes basic commands" do
54
+ routes[method_name].each { |cmd| is_expected.to route_command(cmd).to(method_name) }
55
+ end
56
+ end
57
+ end
58
+
59
+ describe "Unauthorized user access" do
60
+ %i[
61
+ list_teams
62
+ create_alias
63
+ remove_alias
64
+ list_services
65
+ ].each do |method_name|
66
+ it "does not route admin commands" do
67
+ routes[method_name].each { |cmd| is_expected.not_to route_command(cmd).to(method_name) }
68
+ end
69
+ end
70
+ end
71
+
72
+ describe "pagerduty_admins access" do
73
+ before do
74
+ robot.auth.add_user_to_group!(user, :pagerduty_admins)
75
+ end
76
+
77
+ routes.each do |method_name, cmds|
78
+ it "routes all commands" do
79
+ cmds.each { |cmd| is_expected.to route_command(cmd).to(method_name) }
80
+ end
81
+ end
82
+
83
+ after do
84
+ robot.auth.remove_user_from_group!(user, :pagerduty_admins)
85
+ end
86
+ end
87
+
88
+ let(:success) do
89
+ pd_client = double
90
+ events_client = double
91
+
92
+ allow(pd_client).to receive(:get).with('services') do
93
+ # This is not a full response for getting services since there's a lot
94
+ # more metadata in those.
95
+ {
96
+ "limit" => 100,
97
+ "offset" => 0,
98
+ "total" => nil,
99
+ "more" => false,
100
+ "services" => [
101
+ {
102
+ "id" => "PT2JFN8",
103
+ "name" => "PagerDuty Service",
104
+ "status" => "active",
105
+ "type" => "service",
106
+ "self" => "https://api.pagerduty.com/services/PT2JFN8",
107
+ "html_url" => "https://teamname.pagerduty.com/services/PT2JFN8",
108
+
109
+ "teams" => [
110
+ {
111
+ "id" => "PSODFXI",
112
+ "type" => "team_reference",
113
+ "summary" => "Team",
114
+ "self" => "https://api.pagerduty.com/teams/PSODFXI",
115
+ "html_url" => "https://teamname.pagerduty.com/teams/PSODFXI",
116
+ },
117
+ ],
118
+
119
+ "escalation_policy" =>
120
+ {
121
+ "id" => "PEN4ZAY",
122
+ "type" => "escalation_policy_reference",
123
+ "summary" => "First Responder",
124
+ "self" => "https://api.pagerduty.com/escalation_policies/PEN4ZAY",
125
+ "html_url" => "https://teamname.pagerduty.com/escalation_policies/PEN4ZAY",
126
+ },
127
+
128
+ "integrations" => [
129
+ {
130
+ "id" => "PS1XI4R",
131
+ "type" => "generic_email_inbound_integration_reference",
132
+ "summary" => "Email",
133
+ "self" => "https://api.pagerduty.com/services/PT2JFN8/integrations/PS1XI4R",
134
+ "html_url" => "https://teamname.pagerduty.com/services/PT2JFN8/integrations/PS1XI4R",
135
+ },
136
+ ],
137
+ }
138
+ ],
139
+ }
140
+ end
141
+ end
142
+
143
+ end
@@ -0,0 +1,15 @@
1
+ require "simplecov"
2
+ require "coveralls"
3
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(
4
+ [
5
+ SimpleCov::Formatter::HTMLFormatter,
6
+ Coveralls::SimpleCov::Formatter
7
+ ])
8
+ SimpleCov.start { add_filter "/spec/" }
9
+
10
+ require "lita-interrupt"
11
+ require "lita/rspec"
12
+
13
+ # A compatibility mode is provided for older plugins upgrading from Lita 3. Since this plugin
14
+ # was generated with Lita 4, the compatibility mode should be left disabled.
15
+ Lita.version_3_compatibility_mode = false
metadata ADDED
@@ -0,0 +1,201 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lita-interrupt
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Noel Cower
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-07-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: lita
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '4.7'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '4.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pager_duty-connection
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: coveralls
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry-byebug
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rack-test
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rspec
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: 3.0.0
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: 3.0.0
125
+ - !ruby/object:Gem::Dependency
126
+ name: rubocop
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: simplecov
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ description: lita-interrupt interrupts a user by assigning them to a new incident
154
+ in PagerDuty
155
+ email:
156
+ - ncower@gmail.com
157
+ executables: []
158
+ extensions: []
159
+ extra_rdoc_files: []
160
+ files:
161
+ - ".gitignore"
162
+ - ".travis.yml"
163
+ - Gemfile
164
+ - LICENSE
165
+ - README.md
166
+ - Rakefile
167
+ - lib/interrupt_helper/pager_duty.rb
168
+ - lib/lita-interrupt.rb
169
+ - lib/lita/handlers/interrupt.rb
170
+ - lita-interrupt.gemspec
171
+ - locales/en.yml
172
+ - spec/lita/handlers/interrupt_spec.rb
173
+ - spec/spec_helper.rb
174
+ homepage: https://github.com/nilium/lita-interrupt
175
+ licenses:
176
+ - BSD-3-Clause
177
+ metadata:
178
+ lita_plugin_type: handler
179
+ post_install_message:
180
+ rdoc_options: []
181
+ require_paths:
182
+ - lib
183
+ required_ruby_version: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - "~>"
186
+ - !ruby/object:Gem::Version
187
+ version: '2.3'
188
+ required_rubygems_version: !ruby/object:Gem::Requirement
189
+ requirements:
190
+ - - ">="
191
+ - !ruby/object:Gem::Version
192
+ version: '0'
193
+ requirements: []
194
+ rubyforge_project:
195
+ rubygems_version: 2.5.1
196
+ signing_key:
197
+ specification_version: 4
198
+ summary: Interrupts people through the power of PagerDuty
199
+ test_files:
200
+ - spec/lita/handlers/interrupt_spec.rb
201
+ - spec/spec_helper.rb