stoplight 3.0.0 → 3.0.2
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.
- 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 +20 -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 +40 -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: bbfed26c7b63f56e88cd970d5db820022a89d689570cd4e98168b7226d718791
|
4
|
+
data.tar.gz: 9206b2c2a66e78d39f37e7932fc95faaf850a2cc54d024eab2a2d90dc14396b4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 34d477b3b8e0c6e9b34a0a1ea7e0e6fab4950601d914407063a113f019b9e4a7420b4b291f76702e326b41f3b7cd780adb55f9c2df28bd659c9be7d384af35b4
|
7
|
+
data.tar.gz: ab49c8f6cac59358b78807fa82e88e8ee3cec5c668dad2f6cc0ca8183507dc64373f611c23b83dce4d2d5958f176d6c447e796a9137e87a165b0cabd12b7dc7c
|
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?
|
@@ -48,7 +52,18 @@ module Stoplight
|
|
48
52
|
fallback.call(nil)
|
49
53
|
end
|
50
54
|
|
55
|
+
MISSING_BLOCK_ERROR = <<~ERROR
|
56
|
+
Oops! An error occurred while executing the `Stoplight#run` method. This happened because you
|
57
|
+
didn't pass a code block to the `Stoplight()` function. You can fix this issue this way:
|
58
|
+
|
59
|
+
Stoplight('test-light') { ... }.run
|
60
|
+
|
61
|
+
For more details and examples, please refer to the documentation https://github.com/bolshakov/stoplight/tree/release/v3.x
|
62
|
+
ERROR
|
63
|
+
|
51
64
|
def run_code(on_success, on_failure)
|
65
|
+
raise ArgumentError, MISSING_BLOCK_ERROR unless code
|
66
|
+
|
52
67
|
result = code.call
|
53
68
|
failures = clear_failures
|
54
69
|
on_success&.call(failures)
|
@@ -75,8 +90,10 @@ module Stoplight
|
|
75
90
|
end
|
76
91
|
|
77
92
|
def notify(from_color, to_color, error = nil)
|
78
|
-
|
79
|
-
|
93
|
+
data_store.with_notification_lock(self, from_color, to_color) do
|
94
|
+
notifiers.each do |notifier|
|
95
|
+
safely { notifier.notify(self, from_color, to_color, error) }
|
96
|
+
end
|
80
97
|
end
|
81
98
|
end
|
82
99
|
|
@@ -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('')
|
@@ -253,5 +285,13 @@ RSpec.describe Stoplight::Light::Runnable do
|
|
253
285
|
end
|
254
286
|
end
|
255
287
|
end
|
288
|
+
|
289
|
+
context 'when the code block is missing' do
|
290
|
+
subject { Stoplight::Light.new(name) }
|
291
|
+
|
292
|
+
it 'raises an ArgumentError error' do
|
293
|
+
expect { subject.run }.to raise_error(ArgumentError)
|
294
|
+
end
|
295
|
+
end
|
256
296
|
end
|
257
297
|
end
|
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.2
|
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:
|
13
|
+
date: 2023-08-28 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
|