stoplight 3.0.0 → 3.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +0 -18
- data/lib/stoplight/data_store/base.rb +9 -0
- data/lib/stoplight/data_store/memory.rb +26 -0
- data/lib/stoplight/data_store/redis.rb +38 -1
- data/lib/stoplight/light/runnable.rb +9 -3
- data/lib/stoplight/notifier/bugsnag.rb +1 -1
- data/lib/stoplight/version.rb +1 -1
- data/lib/stoplight.rb +1 -2
- data/spec/stoplight/data_store/base_spec.rb +7 -0
- data/spec/stoplight/data_store/memory_spec.rb +26 -0
- data/spec/stoplight/data_store/redis_spec.rb +36 -5
- data/spec/stoplight/light/runnable_spec.rb +32 -0
- metadata +31 -20
- data/lib/stoplight/notifier/hip_chat.rb +0 -43
- data/spec/stoplight/notifier/hip_chat_spec.rb +0 -91
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4ec8f5b8b13778f792ad1fbbbc3138229ef51ac4e62149f93eb3a8b4bccc9d3a
|
4
|
+
data.tar.gz: d93e27dbf0563bb30a7025bf646befe712269c4bd5f259432ffeeb8c680934da
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c3c10ff2886343b2625c777c5143656624df7591ab75e3bb8c8c43032fc10a92dacd9f2a801eebd7c636c5adc5a744525be76cb22f4acb4f839f6a87a6f8608b
|
7
|
+
data.tar.gz: 28af6a4a57af91a6d21df0b79909a4584acca9e5028079aff313911a965d3cb1da36b3a0b9c934b8621b6bd372e84da34d1fa47b47c043c10e27bb99a885c6f9
|
data/README.md
CHANGED
@@ -28,7 +28,6 @@ Check out [stoplight-admin][] for controlling your stoplights.
|
|
28
28
|
- [Redis](#redis)
|
29
29
|
- [Notifiers](#notifiers)
|
30
30
|
- [Bugsnag](#bugsnag)
|
31
|
-
- [HipChat](#hipchat)
|
32
31
|
- [Honeybadger](#honeybadger)
|
33
32
|
- [Logger](#logger)
|
34
33
|
- [Pagerduty](#pagerduty)
|
@@ -311,22 +310,6 @@ Stoplight::Light.default_notifiers += [notifier]
|
|
311
310
|
# => [#<Stoplight::Notifier::IO:...>, #<Stoplight::Notifier::Bugsnag:...>]
|
312
311
|
```
|
313
312
|
|
314
|
-
#### HipChat
|
315
|
-
|
316
|
-
Make sure you have [the HipChat gem][] (`~> 1.5`) installed before configuring
|
317
|
-
Stoplight.
|
318
|
-
|
319
|
-
``` rb
|
320
|
-
require 'hipchat'
|
321
|
-
# => true
|
322
|
-
hip_chat = HipChat::Client.new('token')
|
323
|
-
# => #<HipChat::Client:...>
|
324
|
-
notifier = Stoplight::Notifier::HipChat.new(hip_chat, 'room')
|
325
|
-
# => #<Stoplight::Notifier::HipChat:...>
|
326
|
-
Stoplight::Light.default_notifiers += [notifier]
|
327
|
-
# => [#<Stoplight::Notifier::IO:...>, #<Stoplight::Notifier::HipChat:...>]
|
328
|
-
```
|
329
|
-
|
330
313
|
#### Honeybadger
|
331
314
|
|
332
315
|
Make sure you have [the Honeybadger gem][] (`~> 2.5`) installed before
|
@@ -516,7 +499,6 @@ Stoplight is licensed under [the MIT License][].
|
|
516
499
|
[the cool off time section]: #custom-cool-off-time
|
517
500
|
[the Redis gem]: https://rubygems.org/gems/redis
|
518
501
|
[the Bugsnag gem]: https://rubygems.org/gems/bugsnag
|
519
|
-
[the HipChat gem]: https://rubygems.org/gems/hipchat
|
520
502
|
[the Honeybadger gem]: https://rubygems.org/gems/honeybadger
|
521
503
|
[the Logger class]: http://ruby-doc.org/stdlib-2.2.3/libdoc/logger/rdoc/Logger.html
|
522
504
|
[the Rollbar gem]: https://rubygems.org/gems/rollbar
|
@@ -52,6 +52,15 @@ module Stoplight
|
|
52
52
|
def clear_state(_light)
|
53
53
|
raise NotImplementedError
|
54
54
|
end
|
55
|
+
|
56
|
+
# @param _light [Light]
|
57
|
+
# @param _from_color [String]
|
58
|
+
# @param _to_color [String]
|
59
|
+
# @yield _block
|
60
|
+
# @return [Void]
|
61
|
+
def with_notification_lock(_light, _from_color, _to_color, &_block)
|
62
|
+
raise NotImplementedError
|
63
|
+
end
|
55
64
|
end
|
56
65
|
end
|
57
66
|
end
|
@@ -7,10 +7,12 @@ module Stoplight
|
|
7
7
|
# @see Base
|
8
8
|
class Memory < Base
|
9
9
|
include MonitorMixin
|
10
|
+
KEY_SEPARATOR = ':'
|
10
11
|
|
11
12
|
def initialize
|
12
13
|
@failures = Hash.new { |h, k| h[k] = [] }
|
13
14
|
@states = Hash.new { |h, k| h[k] = State::UNLOCKED }
|
15
|
+
@last_notifications = {}
|
14
16
|
super() # MonitorMixin
|
15
17
|
end
|
16
18
|
|
@@ -49,6 +51,30 @@ module Stoplight
|
|
49
51
|
def clear_state(light)
|
50
52
|
synchronize { @states.delete(light.name) }
|
51
53
|
end
|
54
|
+
|
55
|
+
def with_notification_lock(light, from_color, to_color)
|
56
|
+
synchronize do
|
57
|
+
if last_notification(light) != [from_color, to_color]
|
58
|
+
set_last_notification(light, from_color, to_color)
|
59
|
+
|
60
|
+
yield
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# @param light [Stoplight::Light]
|
66
|
+
# @return [Array, nil]
|
67
|
+
def last_notification(light)
|
68
|
+
@last_notifications[light.name]
|
69
|
+
end
|
70
|
+
|
71
|
+
# @param light [Stoplight::Light]
|
72
|
+
# @param from_color [String]
|
73
|
+
# @param to_color [String]
|
74
|
+
# @return [void]
|
75
|
+
def set_last_notification(light, from_color, to_color)
|
76
|
+
@last_notifications[light.name] = [from_color, to_color]
|
77
|
+
end
|
52
78
|
end
|
53
79
|
end
|
54
80
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'redlock'
|
4
|
+
|
3
5
|
module Stoplight
|
4
6
|
module DataStore
|
5
7
|
# @see Base
|
@@ -8,8 +10,9 @@ module Stoplight
|
|
8
10
|
KEY_SEPARATOR = ':'
|
9
11
|
|
10
12
|
# @param redis [::Redis]
|
11
|
-
def initialize(redis)
|
13
|
+
def initialize(redis, redlock: Redlock::Client.new([redis]))
|
12
14
|
@redis = redis
|
15
|
+
@redlock = redlock
|
13
16
|
end
|
14
17
|
|
15
18
|
def names
|
@@ -76,8 +79,34 @@ module Stoplight
|
|
76
79
|
normalize_state(state)
|
77
80
|
end
|
78
81
|
|
82
|
+
LOCK_TTL = 2_000 # milliseconds
|
83
|
+
|
84
|
+
def with_notification_lock(light, from_color, to_color)
|
85
|
+
@redlock.lock(notification_lock_key(light), LOCK_TTL) do
|
86
|
+
if last_notification(light) != [from_color, to_color]
|
87
|
+
set_last_notification(light, from_color, to_color)
|
88
|
+
|
89
|
+
yield
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
79
94
|
private
|
80
95
|
|
96
|
+
# @param light [Stoplight::Light]
|
97
|
+
# @return [Array, nil]
|
98
|
+
def last_notification(light)
|
99
|
+
@redis.get(last_notification_key(light))&.split('->')
|
100
|
+
end
|
101
|
+
|
102
|
+
# @param light [Stoplight::Light]
|
103
|
+
# @param from_color [String]
|
104
|
+
# @param to_color [String]
|
105
|
+
# @return [void]
|
106
|
+
def set_last_notification(light, from_color, to_color)
|
107
|
+
@redis.set(last_notification_key(light), [from_color, to_color].join('->'))
|
108
|
+
end
|
109
|
+
|
81
110
|
def query_failures(light, transaction: @redis)
|
82
111
|
transaction.lrange(failures_key(light), 0, -1)
|
83
112
|
end
|
@@ -103,6 +132,14 @@ module Stoplight
|
|
103
132
|
key('failures', light.name)
|
104
133
|
end
|
105
134
|
|
135
|
+
def notification_lock_key(light)
|
136
|
+
key('notification_lock', light.name)
|
137
|
+
end
|
138
|
+
|
139
|
+
def last_notification_key(light)
|
140
|
+
key('last_notification', light.name)
|
141
|
+
end
|
142
|
+
|
106
143
|
def states_key
|
107
144
|
key('states')
|
108
145
|
end
|
@@ -30,11 +30,15 @@ module Stoplight
|
|
30
30
|
|
31
31
|
def run_green
|
32
32
|
on_failure = lambda do |size, error|
|
33
|
-
notify(Color::GREEN, Color::RED, error) if size
|
33
|
+
notify(Color::GREEN, Color::RED, error) if failures_threshold_breached?(size, threshold)
|
34
34
|
end
|
35
35
|
run_code(nil, on_failure)
|
36
36
|
end
|
37
37
|
|
38
|
+
def failures_threshold_breached?(current_failures_count, max_errors_threshold)
|
39
|
+
current_failures_count == max_errors_threshold
|
40
|
+
end
|
41
|
+
|
38
42
|
def run_yellow
|
39
43
|
on_success = lambda do |failures|
|
40
44
|
notify(Color::RED, Color::GREEN) unless failures.empty?
|
@@ -75,8 +79,10 @@ module Stoplight
|
|
75
79
|
end
|
76
80
|
|
77
81
|
def notify(from_color, to_color, error = nil)
|
78
|
-
|
79
|
-
|
82
|
+
data_store.with_notification_lock(self, from_color, to_color) do
|
83
|
+
notifiers.each do |notifier|
|
84
|
+
safely { notifier.notify(self, from_color, to_color, error) }
|
85
|
+
end
|
80
86
|
end
|
81
87
|
end
|
82
88
|
|
@@ -29,7 +29,7 @@ module Stoplight
|
|
29
29
|
|
30
30
|
def notify(light, from_color, to_color, error)
|
31
31
|
message = formatter.call(light, from_color, to_color, error)
|
32
|
-
bugsnag.notify(StoplightStatusChange.new(message), options)
|
32
|
+
bugsnag.notify(StoplightStatusChange.new(message), **options)
|
33
33
|
message
|
34
34
|
end
|
35
35
|
end
|
data/lib/stoplight/version.rb
CHANGED
data/lib/stoplight.rb
CHANGED
@@ -20,7 +20,6 @@ require 'stoplight/notifier/base'
|
|
20
20
|
require 'stoplight/notifier/generic'
|
21
21
|
|
22
22
|
require 'stoplight/notifier/bugsnag'
|
23
|
-
require 'stoplight/notifier/hip_chat'
|
24
23
|
require 'stoplight/notifier/honeybadger'
|
25
24
|
require 'stoplight/notifier/io'
|
26
25
|
require 'stoplight/notifier/logger'
|
@@ -35,6 +34,6 @@ require 'stoplight/light/runnable'
|
|
35
34
|
require 'stoplight/light'
|
36
35
|
|
37
36
|
# @see Stoplight::Light#initialize
|
38
|
-
def Stoplight(name, &code) # rubocop:disable
|
37
|
+
def Stoplight(name, &code) # rubocop:disable Naming/MethodName
|
39
38
|
Stoplight::Light.new(name, &code)
|
40
39
|
end
|
@@ -61,4 +61,11 @@ RSpec.describe Stoplight::DataStore::Base do
|
|
61
61
|
.to raise_error(NotImplementedError)
|
62
62
|
end
|
63
63
|
end
|
64
|
+
|
65
|
+
describe '#with_notification_lock' do
|
66
|
+
it 'is not implemented' do
|
67
|
+
expect { data_store.with_notification_lock(nil, nil, nil) }
|
68
|
+
.to raise_error(NotImplementedError)
|
69
|
+
end
|
70
|
+
end
|
64
71
|
end
|
@@ -130,4 +130,30 @@ RSpec.describe Stoplight::DataStore::Memory do
|
|
130
130
|
expect(data_store.get_state(light)).to eql(Stoplight::State::UNLOCKED)
|
131
131
|
end
|
132
132
|
end
|
133
|
+
|
134
|
+
describe '#with_notification_lock' do
|
135
|
+
context 'when notification is already sent' do
|
136
|
+
before do
|
137
|
+
data_store.with_notification_lock(light, Stoplight::Color::GREEN, Stoplight::Color::RED) {}
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'does not yield passed block' do
|
141
|
+
expect do |b|
|
142
|
+
data_store.with_notification_lock(light, Stoplight::Color::GREEN, Stoplight::Color::RED, &b)
|
143
|
+
end.not_to yield_control
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
context 'when notification is not already sent' do
|
148
|
+
before do
|
149
|
+
data_store.with_notification_lock(light, Stoplight::Color::GREEN, Stoplight::Color::RED) {}
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'yields passed block' do
|
153
|
+
expect do |b|
|
154
|
+
data_store.with_notification_lock(light, Stoplight::Color::RED, Stoplight::Color::GREEN, &b)
|
155
|
+
end.to yield_control
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
133
159
|
end
|
@@ -1,17 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'spec_helper'
|
4
|
-
require '
|
4
|
+
require 'mock_redis'
|
5
5
|
|
6
6
|
RSpec.describe Stoplight::DataStore::Redis do
|
7
|
-
let(:data_store) { described_class.new(redis) }
|
8
|
-
let(:redis) {
|
7
|
+
let(:data_store) { described_class.new(redis, redlock: redlock) }
|
8
|
+
let(:redis) { MockRedis.new }
|
9
|
+
let(:redlock) { instance_double(Redlock::Client) }
|
9
10
|
let(:light) { Stoplight::Light.new(name) {} }
|
10
11
|
let(:name) { ('a'..'z').to_a.shuffle.join }
|
11
12
|
let(:failure) { Stoplight::Failure.new('class', 'message', Time.new) }
|
12
13
|
|
13
|
-
before { Redis::Connection::Memory.reset_all_databases }
|
14
|
-
|
15
14
|
it 'is a class' do
|
16
15
|
expect(described_class).to be_a(Class)
|
17
16
|
end
|
@@ -143,4 +142,36 @@ RSpec.describe Stoplight::DataStore::Redis do
|
|
143
142
|
expect(data_store.get_state(light)).to eql(Stoplight::State::UNLOCKED)
|
144
143
|
end
|
145
144
|
end
|
145
|
+
|
146
|
+
describe '#with_notification_lock' do
|
147
|
+
let(:lock_key) { "stoplight:notification_lock:#{name}" }
|
148
|
+
|
149
|
+
before do
|
150
|
+
allow(redlock).to receive(:lock).with(lock_key, 2_000).and_yield
|
151
|
+
end
|
152
|
+
|
153
|
+
context 'when notification is already sent' do
|
154
|
+
before do
|
155
|
+
data_store.with_notification_lock(light, Stoplight::Color::GREEN, Stoplight::Color::RED) {}
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'does not yield passed block' do
|
159
|
+
expect do |b|
|
160
|
+
data_store.with_notification_lock(light, Stoplight::Color::GREEN, Stoplight::Color::RED, &b)
|
161
|
+
end.not_to yield_control
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
context 'when notification is not already sent' do
|
166
|
+
before do
|
167
|
+
data_store.with_notification_lock(light, Stoplight::Color::GREEN, Stoplight::Color::RED) {}
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'yields passed block' do
|
171
|
+
expect do |b|
|
172
|
+
data_store.with_notification_lock(light, Stoplight::Color::RED, Stoplight::Color::GREEN, &b)
|
173
|
+
end.to yield_control
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
146
177
|
end
|
@@ -109,6 +109,38 @@ RSpec.describe Stoplight::Light::Runnable do
|
|
109
109
|
expect(subject.data_store.get_failures(subject).size).to eql(1)
|
110
110
|
end
|
111
111
|
|
112
|
+
context 'when we did not send notifications yet' do
|
113
|
+
it 'notifies when transitioning to red' do
|
114
|
+
subject.threshold.times do
|
115
|
+
expect(io.string).to eql('')
|
116
|
+
begin
|
117
|
+
subject.run
|
118
|
+
rescue error.class
|
119
|
+
nil
|
120
|
+
end
|
121
|
+
end
|
122
|
+
expect(io.string).to_not eql('')
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context 'when we already sent notifications' do
|
127
|
+
before do
|
128
|
+
subject.data_store.with_notification_lock(subject, Stoplight::Color::GREEN, Stoplight::Color::RED) {}
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'does not send new notifications' do
|
132
|
+
subject.threshold.times do
|
133
|
+
expect(io.string).to eql('')
|
134
|
+
begin
|
135
|
+
subject.run
|
136
|
+
rescue error.class
|
137
|
+
nil
|
138
|
+
end
|
139
|
+
end
|
140
|
+
expect(io.string).to eql('')
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
112
144
|
it 'notifies when transitioning to red' do
|
113
145
|
subject.threshold.times do
|
114
146
|
expect(io.string).to eql('')
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stoplight
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.
|
4
|
+
version: 3.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cameron Desautels
|
@@ -10,8 +10,22 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2022-
|
13
|
+
date: 2022-12-01 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: redlock
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - "~>"
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - "~>"
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '1.0'
|
15
29
|
- !ruby/object:Gem::Dependency
|
16
30
|
name: benchmark-ips
|
17
31
|
requirement: !ruby/object:Gem::Requirement
|
@@ -46,42 +60,42 @@ dependencies:
|
|
46
60
|
requirements:
|
47
61
|
- - "~>"
|
48
62
|
- !ruby/object:Gem::Version
|
49
|
-
version: '0.
|
63
|
+
version: '0.8'
|
50
64
|
type: :development
|
51
65
|
prerelease: false
|
52
66
|
version_requirements: !ruby/object:Gem::Requirement
|
53
67
|
requirements:
|
54
68
|
- - "~>"
|
55
69
|
- !ruby/object:Gem::Version
|
56
|
-
version: '0.
|
70
|
+
version: '0.8'
|
57
71
|
- !ruby/object:Gem::Dependency
|
58
|
-
name:
|
72
|
+
name: honeybadger
|
59
73
|
requirement: !ruby/object:Gem::Requirement
|
60
74
|
requirements:
|
61
75
|
- - "~>"
|
62
76
|
- !ruby/object:Gem::Version
|
63
|
-
version: '
|
77
|
+
version: '2.5'
|
64
78
|
type: :development
|
65
79
|
prerelease: false
|
66
80
|
version_requirements: !ruby/object:Gem::Requirement
|
67
81
|
requirements:
|
68
82
|
- - "~>"
|
69
83
|
- !ruby/object:Gem::Version
|
70
|
-
version: '
|
84
|
+
version: '2.5'
|
71
85
|
- !ruby/object:Gem::Dependency
|
72
|
-
name:
|
86
|
+
name: mock_redis
|
73
87
|
requirement: !ruby/object:Gem::Requirement
|
74
88
|
requirements:
|
75
89
|
- - "~>"
|
76
90
|
- !ruby/object:Gem::Version
|
77
|
-
version: '
|
91
|
+
version: '0.3'
|
78
92
|
type: :development
|
79
93
|
prerelease: false
|
80
94
|
version_requirements: !ruby/object:Gem::Requirement
|
81
95
|
requirements:
|
82
96
|
- - "~>"
|
83
97
|
- !ruby/object:Gem::Version
|
84
|
-
version: '
|
98
|
+
version: '0.3'
|
85
99
|
- !ruby/object:Gem::Dependency
|
86
100
|
name: pagerduty
|
87
101
|
requirement: !ruby/object:Gem::Requirement
|
@@ -116,28 +130,28 @@ dependencies:
|
|
116
130
|
requirements:
|
117
131
|
- - "~>"
|
118
132
|
- !ruby/object:Gem::Version
|
119
|
-
version: '
|
133
|
+
version: '4.1'
|
120
134
|
type: :development
|
121
135
|
prerelease: false
|
122
136
|
version_requirements: !ruby/object:Gem::Requirement
|
123
137
|
requirements:
|
124
138
|
- - "~>"
|
125
139
|
- !ruby/object:Gem::Version
|
126
|
-
version: '
|
140
|
+
version: '4.1'
|
127
141
|
- !ruby/object:Gem::Dependency
|
128
142
|
name: rspec
|
129
143
|
requirement: !ruby/object:Gem::Requirement
|
130
144
|
requirements:
|
131
145
|
- - "~>"
|
132
146
|
- !ruby/object:Gem::Version
|
133
|
-
version: '3.
|
147
|
+
version: '3.11'
|
134
148
|
type: :development
|
135
149
|
prerelease: false
|
136
150
|
version_requirements: !ruby/object:Gem::Requirement
|
137
151
|
requirements:
|
138
152
|
- - "~>"
|
139
153
|
- !ruby/object:Gem::Version
|
140
|
-
version: '3.
|
154
|
+
version: '3.11'
|
141
155
|
- !ruby/object:Gem::Dependency
|
142
156
|
name: rubocop
|
143
157
|
requirement: !ruby/object:Gem::Requirement
|
@@ -214,14 +228,14 @@ dependencies:
|
|
214
228
|
requirements:
|
215
229
|
- - "~>"
|
216
230
|
- !ruby/object:Gem::Version
|
217
|
-
version: '0.
|
231
|
+
version: '0.9'
|
218
232
|
type: :development
|
219
233
|
prerelease: false
|
220
234
|
version_requirements: !ruby/object:Gem::Requirement
|
221
235
|
requirements:
|
222
236
|
- - "~>"
|
223
237
|
- !ruby/object:Gem::Version
|
224
|
-
version: '0.
|
238
|
+
version: '0.9'
|
225
239
|
description: An implementation of the circuit breaker pattern.
|
226
240
|
email:
|
227
241
|
- camdez@gmail.com
|
@@ -249,7 +263,6 @@ files:
|
|
249
263
|
- lib/stoplight/notifier/base.rb
|
250
264
|
- lib/stoplight/notifier/bugsnag.rb
|
251
265
|
- lib/stoplight/notifier/generic.rb
|
252
|
-
- lib/stoplight/notifier/hip_chat.rb
|
253
266
|
- lib/stoplight/notifier/honeybadger.rb
|
254
267
|
- lib/stoplight/notifier/io.rb
|
255
268
|
- lib/stoplight/notifier/logger.rb
|
@@ -273,7 +286,6 @@ files:
|
|
273
286
|
- spec/stoplight/notifier/base_spec.rb
|
274
287
|
- spec/stoplight/notifier/bugsnag_spec.rb
|
275
288
|
- spec/stoplight/notifier/generic_spec.rb
|
276
|
-
- spec/stoplight/notifier/hip_chat_spec.rb
|
277
289
|
- spec/stoplight/notifier/honeybadger_spec.rb
|
278
290
|
- spec/stoplight/notifier/io_spec.rb
|
279
291
|
- spec/stoplight/notifier/logger_spec.rb
|
@@ -304,7 +316,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
304
316
|
- !ruby/object:Gem::Version
|
305
317
|
version: '0'
|
306
318
|
requirements: []
|
307
|
-
rubygems_version: 3.
|
319
|
+
rubygems_version: 3.3.7
|
308
320
|
signing_key:
|
309
321
|
specification_version: 4
|
310
322
|
summary: Traffic control for code.
|
@@ -323,7 +335,6 @@ test_files:
|
|
323
335
|
- spec/stoplight/notifier/base_spec.rb
|
324
336
|
- spec/stoplight/notifier/bugsnag_spec.rb
|
325
337
|
- spec/stoplight/notifier/generic_spec.rb
|
326
|
-
- spec/stoplight/notifier/hip_chat_spec.rb
|
327
338
|
- spec/stoplight/notifier/honeybadger_spec.rb
|
328
339
|
- spec/stoplight/notifier/io_spec.rb
|
329
340
|
- spec/stoplight/notifier/logger_spec.rb
|
@@ -1,43 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Stoplight
|
4
|
-
module Notifier
|
5
|
-
# @see Base
|
6
|
-
class HipChat < Base
|
7
|
-
DEFAULT_OPTIONS = {
|
8
|
-
color: 'purple',
|
9
|
-
message_format: 'text',
|
10
|
-
notify: true
|
11
|
-
}.freeze
|
12
|
-
|
13
|
-
# @return [Proc]
|
14
|
-
attr_reader :formatter
|
15
|
-
# @return [::HipChat::Client]
|
16
|
-
attr_reader :hip_chat
|
17
|
-
# @return [Hash{Symbol => Object}]
|
18
|
-
attr_reader :options
|
19
|
-
# @return [String]
|
20
|
-
attr_reader :room
|
21
|
-
|
22
|
-
# @param hip_chat [::HipChat::Client]
|
23
|
-
# @param room [String]
|
24
|
-
# @param formatter [Proc, nil]
|
25
|
-
# @param options [Hash{Symbol => Object}]
|
26
|
-
# @option options [String] :color
|
27
|
-
# @option options [String] :message_format
|
28
|
-
# @option options [Boolean] :notify
|
29
|
-
def initialize(hip_chat, room, formatter = nil, options = {})
|
30
|
-
@hip_chat = hip_chat
|
31
|
-
@room = room
|
32
|
-
@formatter = formatter || Default::FORMATTER
|
33
|
-
@options = DEFAULT_OPTIONS.merge(options)
|
34
|
-
end
|
35
|
-
|
36
|
-
def notify(light, from_color, to_color, error)
|
37
|
-
message = formatter.call(light, from_color, to_color, error)
|
38
|
-
hip_chat[room].send('Stoplight', message, options)
|
39
|
-
message
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
@@ -1,91 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
# require 'hipchat'
|
6
|
-
module HipChat
|
7
|
-
class Client
|
8
|
-
def initialize(*); end
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
RSpec.describe Stoplight::Notifier::HipChat do
|
13
|
-
it 'is a class' do
|
14
|
-
expect(described_class).to be_a(Class)
|
15
|
-
end
|
16
|
-
|
17
|
-
it 'is a subclass of Base' do
|
18
|
-
expect(described_class).to be < Stoplight::Notifier::Base
|
19
|
-
end
|
20
|
-
|
21
|
-
describe '#formatter' do
|
22
|
-
it 'is initially the default' do
|
23
|
-
expect(described_class.new(nil, nil).formatter)
|
24
|
-
.to eql(Stoplight::Default::FORMATTER)
|
25
|
-
end
|
26
|
-
|
27
|
-
it 'reads the formatter' do
|
28
|
-
formatter = proc {}
|
29
|
-
expect(described_class.new(nil, nil, formatter).formatter)
|
30
|
-
.to eql(formatter)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
describe '#hip_chat' do
|
35
|
-
it 'reads the HipChat client' do
|
36
|
-
hip_chat = HipChat::Client.new('API token')
|
37
|
-
expect(described_class.new(hip_chat, nil).hip_chat)
|
38
|
-
.to eql(hip_chat)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
describe '#options' do
|
43
|
-
it 'is initially the default' do
|
44
|
-
expect(described_class.new(nil, nil).options)
|
45
|
-
.to eql(Stoplight::Notifier::HipChat::DEFAULT_OPTIONS)
|
46
|
-
end
|
47
|
-
|
48
|
-
it 'reads the options' do
|
49
|
-
options = { key: :value }
|
50
|
-
expect(described_class.new(nil, nil, nil, options).options)
|
51
|
-
.to eql(Stoplight::Notifier::HipChat::DEFAULT_OPTIONS.merge(options))
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
describe '#room' do
|
56
|
-
it 'reads the room' do
|
57
|
-
room = 'Notifications'
|
58
|
-
expect(described_class.new(nil, room).room).to eql(room)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
describe '#notify' do
|
63
|
-
let(:light) { Stoplight::Light.new(name, &code) }
|
64
|
-
let(:name) { ('a'..'z').to_a.shuffle.join }
|
65
|
-
let(:code) { -> {} }
|
66
|
-
let(:from_color) { Stoplight::Color::GREEN }
|
67
|
-
let(:to_color) { Stoplight::Color::RED }
|
68
|
-
let(:notifier) { described_class.new(hip_chat, room) }
|
69
|
-
let(:hip_chat) { double(HipChat::Client) }
|
70
|
-
let(:room) { ('a'..'z').to_a.shuffle.join }
|
71
|
-
|
72
|
-
before do
|
73
|
-
tmp = double
|
74
|
-
expect(hip_chat).to receive(:[]).with(room).and_return(tmp)
|
75
|
-
expect(tmp).to receive(:send)
|
76
|
-
.with('Stoplight', kind_of(String), kind_of(Hash)).and_return(true)
|
77
|
-
end
|
78
|
-
|
79
|
-
it 'returns the message' do
|
80
|
-
error = nil
|
81
|
-
expect(notifier.notify(light, from_color, to_color, error))
|
82
|
-
.to eql(notifier.formatter.call(light, from_color, to_color, error))
|
83
|
-
end
|
84
|
-
|
85
|
-
it 'returns the message with an error' do
|
86
|
-
error = ZeroDivisionError.new('divided by 0')
|
87
|
-
expect(notifier.notify(light, from_color, to_color, error))
|
88
|
-
.to eql(notifier.formatter.call(light, from_color, to_color, error))
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|