stoplight-admin 0.4.0.pre1 → 0.4.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.
- checksums.yaml +4 -4
- data/README.md +11 -6
- data/lib/sinatra/stoplight_admin.rb +3 -159
- data/lib/stoplight_admin/actions/action.rb +22 -0
- data/lib/stoplight_admin/actions/home.rb +25 -0
- data/lib/stoplight_admin/actions/lock.rb +21 -0
- data/lib/stoplight_admin/actions/lock_all_green.rb +16 -0
- data/lib/stoplight_admin/actions/lock_green.rb +21 -0
- data/lib/stoplight_admin/actions/lock_red.rb +21 -0
- data/lib/stoplight_admin/actions/stats.rb +8 -0
- data/lib/stoplight_admin/actions/unlock.rb +21 -0
- data/lib/stoplight_admin/application/helpers.rb +17 -0
- data/lib/stoplight_admin/application.rb +63 -0
- data/lib/stoplight_admin/dependencies.rb +61 -0
- data/lib/stoplight_admin/lights_repository/light.rb +64 -0
- data/lib/stoplight_admin/lights_repository.rb +71 -0
- data/lib/stoplight_admin/lights_stats.rb +75 -0
- data/lib/{sinatra → stoplight_admin}/views/index.erb +11 -11
- data/lib/stoplight_admin.rb +21 -0
- metadata +47 -12
- /data/lib/{sinatra → stoplight_admin}/views/layout.erb +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c61065004c1c254f44f10415c852818e962b82ce7d2b7e9d451bd2b998487144
|
4
|
+
data.tar.gz: 3162abbe1ad6f0e0aad93fbade2b35fae6e7df7ddfa2b61a161bc7d339da9c9e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b53b0093a1ac59c7d21352a6ea0ecd7ac3de163d000ab701cf92eb19250f9fdf803cccb1df30b6bf55d721ff12a9ca25d28239ce16c223ded9e9e42f99cf72b3
|
7
|
+
data.tar.gz: e709637df523e89529e507159bff0bffc63b364539ae1e2fe7873b5525f2aded28833d9d45ee6bfc26199b58f8115c43edf0b3fcb283b68487cf444d3becbe50
|
data/README.md
CHANGED
@@ -6,6 +6,9 @@ A simple administration interface for [stoplight][2]. Monitor the
|
|
6
6
|
status, failures, and invocations of your stoplights. Change
|
7
7
|
stoplight colors, or lock them in either red or green state.
|
8
8
|
|
9
|
+
The stoplight-admin version 0.4 and above is only compatible with Stoplight 4.1. If you have Stoplight version
|
10
|
+
below 4.0, use [stoplight-admin 0.3.6].
|
11
|
+
|
9
12
|
## Configuration
|
10
13
|
|
11
14
|
This project is packaged as a Ruby gem so that you can easily embed it
|
@@ -37,13 +40,14 @@ require 'bundler/inline'
|
|
37
40
|
gemfile do
|
38
41
|
source 'https://rubygems.org'
|
39
42
|
|
40
|
-
gem '
|
43
|
+
gem 'redis'
|
41
44
|
gem 'stoplight-admin'
|
45
|
+
gem 'webrick'
|
42
46
|
end
|
43
47
|
|
44
48
|
require 'redis'
|
45
49
|
require 'sinatra'
|
46
|
-
require '
|
50
|
+
require 'stoplight_admin/application'
|
47
51
|
|
48
52
|
redis = Redis.new(url: 'redis://localhost:6379')
|
49
53
|
set :data_store, Stoplight::DataStore::Redis.new(redis)
|
@@ -74,10 +78,10 @@ Add something like this to your `config/routes.rb`:
|
|
74
78
|
|
75
79
|
``` rb
|
76
80
|
require 'redis'
|
77
|
-
require '
|
81
|
+
require 'stoplight_admin/application'
|
78
82
|
|
79
|
-
class
|
80
|
-
register
|
83
|
+
class StoplightAdminApp < Sinatra::Base
|
84
|
+
register StoplightAdmin::Application
|
81
85
|
|
82
86
|
redis = Redis.new # Uses REDIS_URL environment variable.
|
83
87
|
data_store = Stoplight::DataStore::Redis.new(redis)
|
@@ -85,7 +89,7 @@ class StoplightAdmin < Sinatra::Base
|
|
85
89
|
end
|
86
90
|
|
87
91
|
Rails.application.routes.draw do
|
88
|
-
mount
|
92
|
+
mount StoplightAdminApp => '/stoplights'
|
89
93
|
end
|
90
94
|
```
|
91
95
|
|
@@ -102,3 +106,4 @@ Stoplight is brought to you by [@camdez][4] and [@tfausak][5] from
|
|
102
106
|
[6]: https://github.com/OrgSync
|
103
107
|
[7]: https://badge.fury.io/rb/stoplight-admin.svg
|
104
108
|
[8]: https://rubygems.org/gems/stoplight-admin
|
109
|
+
[stoplight-admin 0.3.6]: https://github.com/bolshakov/stoplight-admin/releases/tag/v0.3.6
|
@@ -1,160 +1,4 @@
|
|
1
|
-
|
1
|
+
require "stoplight_admin/application"
|
2
2
|
|
3
|
-
|
4
|
-
require
|
5
|
-
require 'stoplight'
|
6
|
-
|
7
|
-
module Sinatra
|
8
|
-
module StoplightAdmin
|
9
|
-
module Helpers
|
10
|
-
COLORS = [
|
11
|
-
GREEN = Stoplight::Color::GREEN,
|
12
|
-
YELLOW = Stoplight::Color::YELLOW,
|
13
|
-
RED = Stoplight::Color::RED
|
14
|
-
].freeze
|
15
|
-
|
16
|
-
def data_store
|
17
|
-
return @data_store if defined?(@data_store)
|
18
|
-
@data_store = Stoplight::Light.default_data_store = settings.data_store
|
19
|
-
end
|
20
|
-
|
21
|
-
def lights
|
22
|
-
data_store
|
23
|
-
.names
|
24
|
-
.map { |name| light_info(name) }
|
25
|
-
.sort_by { |light| light_sort_key(light) }
|
26
|
-
end
|
27
|
-
|
28
|
-
def light_info(light)
|
29
|
-
l = Stoplight::Light.new(light) {}
|
30
|
-
color = l.color
|
31
|
-
failures, state = l.data_store.get_all(l)
|
32
|
-
|
33
|
-
{
|
34
|
-
name: light,
|
35
|
-
color: color,
|
36
|
-
failures: failures,
|
37
|
-
locked: locked?(state)
|
38
|
-
}
|
39
|
-
end
|
40
|
-
|
41
|
-
def light_sort_key(light)
|
42
|
-
[-COLORS.index(light[:color]),
|
43
|
-
light[:name]]
|
44
|
-
end
|
45
|
-
|
46
|
-
def locked?(state)
|
47
|
-
[Stoplight::State::LOCKED_GREEN,
|
48
|
-
Stoplight::State::LOCKED_RED]
|
49
|
-
.include?(state)
|
50
|
-
end
|
51
|
-
|
52
|
-
def stat_params(ls)
|
53
|
-
h = {
|
54
|
-
count_red: 0, count_yellow: 0, count_green: 0,
|
55
|
-
percent_red: 0, percent_yellow: 0, percent_green: 0
|
56
|
-
}
|
57
|
-
return h if (size = ls.size).zero?
|
58
|
-
|
59
|
-
h[:count_red] = ls.count { |l| l[:color] == RED }
|
60
|
-
h[:count_yellow] = ls.count { |l| l[:color] == YELLOW }
|
61
|
-
h[:count_green] = size - h[:count_red] - h[:count_yellow]
|
62
|
-
|
63
|
-
h[:percent_red] = (100.0 * h[:count_red] / size).ceil
|
64
|
-
h[:percent_yellow] = (100.0 * h[:count_yellow] / size).ceil
|
65
|
-
h[:percent_green] = 100.0 - h[:percent_red] - h[:percent_yellow]
|
66
|
-
|
67
|
-
h
|
68
|
-
end
|
69
|
-
|
70
|
-
def lock(light)
|
71
|
-
l = Stoplight::Light.new(light) {}
|
72
|
-
new_state =
|
73
|
-
case l.color
|
74
|
-
when Stoplight::Color::GREEN
|
75
|
-
Stoplight::State::LOCKED_GREEN
|
76
|
-
else
|
77
|
-
Stoplight::State::LOCKED_RED
|
78
|
-
end
|
79
|
-
|
80
|
-
data_store.set_state(l, new_state)
|
81
|
-
end
|
82
|
-
|
83
|
-
def unlock(light)
|
84
|
-
l = Stoplight::Light.new(light) {}
|
85
|
-
data_store.set_state(l, Stoplight::State::UNLOCKED)
|
86
|
-
end
|
87
|
-
|
88
|
-
def green(light)
|
89
|
-
l = Stoplight::Light.new(light) {}
|
90
|
-
if data_store.get_state(l) == Stoplight::State::LOCKED_RED
|
91
|
-
new_state = Stoplight::State::LOCKED_GREEN
|
92
|
-
data_store.set_state(l, new_state)
|
93
|
-
end
|
94
|
-
|
95
|
-
data_store.clear_failures(l)
|
96
|
-
end
|
97
|
-
|
98
|
-
def red(light)
|
99
|
-
l = Stoplight::Light.new(light) {}
|
100
|
-
data_store.set_state(l, Stoplight::State::LOCKED_RED)
|
101
|
-
end
|
102
|
-
|
103
|
-
def with_lights
|
104
|
-
[*params[:names]]
|
105
|
-
.map { |l| CGI.unescape(l) }
|
106
|
-
.each { |l| yield(l) }
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
def self.registered(app)
|
111
|
-
app.helpers StoplightAdmin::Helpers
|
112
|
-
|
113
|
-
app.set :data_store, nil
|
114
|
-
app.set :views, File.join(File.dirname(__FILE__), 'views')
|
115
|
-
|
116
|
-
app.get '/' do
|
117
|
-
ls = lights
|
118
|
-
stats = stat_params(ls)
|
119
|
-
|
120
|
-
erb :index, locals: stats.merge(lights: ls)
|
121
|
-
end
|
122
|
-
|
123
|
-
app.get '/stats' do
|
124
|
-
ls = lights
|
125
|
-
stats = stat_params(ls)
|
126
|
-
|
127
|
-
json({stats: stats, lights: ls})
|
128
|
-
end
|
129
|
-
|
130
|
-
app.post '/lock' do
|
131
|
-
with_lights { |l| lock(l) }
|
132
|
-
redirect to('/')
|
133
|
-
end
|
134
|
-
|
135
|
-
app.post '/unlock' do
|
136
|
-
with_lights { |l| unlock(l) }
|
137
|
-
redirect to('/')
|
138
|
-
end
|
139
|
-
|
140
|
-
app.post '/green' do
|
141
|
-
with_lights { |l| green(l) }
|
142
|
-
redirect to('/')
|
143
|
-
end
|
144
|
-
|
145
|
-
app.post '/red' do
|
146
|
-
with_lights { |l| red(l) }
|
147
|
-
redirect to('/')
|
148
|
-
end
|
149
|
-
|
150
|
-
app.post '/green_all' do
|
151
|
-
data_store.names
|
152
|
-
.reject { |l| Stoplight::Light.new(l) {}.color == Stoplight::Color::GREEN }
|
153
|
-
.each { |l| green(l) }
|
154
|
-
redirect to('/')
|
155
|
-
end
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
register StoplightAdmin
|
160
|
-
end
|
3
|
+
warn "[DEPRECATED] requiring `sinatra/stoplight_admin` is deprecated. " \
|
4
|
+
"Please require `stoplight_admin/application` instead."
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StoplightAdmin
|
4
|
+
module Actions
|
5
|
+
# @abstract
|
6
|
+
class Action
|
7
|
+
# @!attribute lights_repository
|
8
|
+
# @return [StoplightAdmin::LightsRepository]
|
9
|
+
attr_reader :lights_repository
|
10
|
+
private :lights_repository
|
11
|
+
|
12
|
+
# @return lights_repository [StoplightAdmin::LightsRepository]
|
13
|
+
def initialize(lights_repository:)
|
14
|
+
@lights_repository = lights_repository
|
15
|
+
end
|
16
|
+
|
17
|
+
def call(params)
|
18
|
+
raise NotImplementedError
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StoplightAdmin
|
4
|
+
module Actions
|
5
|
+
class Home < Action
|
6
|
+
# @!attribute lights_stats
|
7
|
+
# @return [Class<StoplightAdmin::LightsStats>]
|
8
|
+
attr_reader :lights_stats
|
9
|
+
private :lights_stats
|
10
|
+
|
11
|
+
# @param lights_stats [Class<StoplightAdmin::LightsStats>]
|
12
|
+
def initialize(lights_stats:, **deps)
|
13
|
+
super(**deps)
|
14
|
+
@lights_stats = lights_stats
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [(StoplightAdmin::LightsRepository::Light)]
|
18
|
+
def call(*)
|
19
|
+
lights = lights_repository.all
|
20
|
+
stats = lights_stats.call(lights)
|
21
|
+
[lights, stats]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StoplightAdmin
|
4
|
+
module Actions
|
5
|
+
# This action locks light
|
6
|
+
class Lock < Action
|
7
|
+
# @param params [Hash] query parameters
|
8
|
+
# @return [void]
|
9
|
+
def call(params)
|
10
|
+
light_names(params).each do |name|
|
11
|
+
lights_repository.lock(name)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private def light_names(params)
|
16
|
+
Array(params[:names])
|
17
|
+
.map { |name| CGI.unescape(name) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StoplightAdmin
|
4
|
+
module Actions
|
5
|
+
# This action locks all lights green
|
6
|
+
class LockAllGreen < Action
|
7
|
+
# @return [void]
|
8
|
+
def call(*)
|
9
|
+
lights_repository
|
10
|
+
.with_color(RED, YELLOW)
|
11
|
+
.map(&:name)
|
12
|
+
.each { |name| lights_repository.lock(name, GREEN) }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StoplightAdmin
|
4
|
+
module Actions
|
5
|
+
# This action locks light with the specific name green
|
6
|
+
class LockGreen < Action
|
7
|
+
# @param params [Hash] query parameters
|
8
|
+
# @return [void]
|
9
|
+
def call(params)
|
10
|
+
light_names(params).each do |name|
|
11
|
+
lights_repository.lock(name, GREEN)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private def light_names(params)
|
16
|
+
Array(params[:names])
|
17
|
+
.map { |name| CGI.unescape(name) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StoplightAdmin
|
4
|
+
module Actions
|
5
|
+
# This action locks light with the specific name red
|
6
|
+
class LockRed < Action
|
7
|
+
# @param params [Hash] query parameters
|
8
|
+
# @return [void]
|
9
|
+
def call(params)
|
10
|
+
light_names(params).each do |name|
|
11
|
+
lights_repository.lock(name, RED)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private def light_names(params)
|
16
|
+
Array(params[:names])
|
17
|
+
.map { |name| CGI.unescape(name) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StoplightAdmin
|
4
|
+
module Actions
|
5
|
+
# This action unlocks light
|
6
|
+
class Unlock < Action
|
7
|
+
# @param params [Hash] query parameters
|
8
|
+
# @return [void]
|
9
|
+
def call(params)
|
10
|
+
light_names(params).each do |name|
|
11
|
+
lights_repository.unlock(name)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private def light_names(params)
|
16
|
+
Array(params[:names])
|
17
|
+
.map { |name| CGI.unescape(name) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StoplightAdmin
|
4
|
+
module Application
|
5
|
+
module Helpers
|
6
|
+
COLORS = [
|
7
|
+
GREEN = Stoplight::Color::GREEN,
|
8
|
+
YELLOW = Stoplight::Color::YELLOW,
|
9
|
+
RED = Stoplight::Color::RED
|
10
|
+
].freeze
|
11
|
+
|
12
|
+
def dependencies
|
13
|
+
StoplightAdmin::Dependencies.new(data_store: settings.data_store)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "sinatra"
|
4
|
+
require "sinatra/base"
|
5
|
+
require "sinatra/json"
|
6
|
+
require "stoplight_admin"
|
7
|
+
|
8
|
+
module StoplightAdmin
|
9
|
+
module Application
|
10
|
+
def self.registered(app)
|
11
|
+
app.helpers Application::Helpers
|
12
|
+
|
13
|
+
app.set :data_store, nil
|
14
|
+
app.set :views, File.join(File.dirname(__FILE__), "views")
|
15
|
+
|
16
|
+
app.get "/" do
|
17
|
+
lights, stats = dependencies.home_action.call
|
18
|
+
|
19
|
+
erb :index, locals: stats.merge(lights: lights)
|
20
|
+
end
|
21
|
+
|
22
|
+
app.get "/stats" do
|
23
|
+
lights, stats = dependencies.stats_action.call
|
24
|
+
|
25
|
+
json({stats: stats, lights: lights.map(&:as_json)})
|
26
|
+
end
|
27
|
+
|
28
|
+
app.post "/lock" do
|
29
|
+
dependencies.lock_action.call(params)
|
30
|
+
|
31
|
+
redirect to("/")
|
32
|
+
end
|
33
|
+
|
34
|
+
app.post "/unlock" do
|
35
|
+
dependencies.unlock_action.call(params)
|
36
|
+
|
37
|
+
redirect to("/")
|
38
|
+
end
|
39
|
+
|
40
|
+
app.post "/green" do
|
41
|
+
dependencies.green_action.call(params)
|
42
|
+
|
43
|
+
redirect to("/")
|
44
|
+
end
|
45
|
+
|
46
|
+
app.post "/red" do
|
47
|
+
dependencies.red_action.call(params)
|
48
|
+
|
49
|
+
redirect to("/")
|
50
|
+
end
|
51
|
+
|
52
|
+
app.post "/green_all" do
|
53
|
+
dependencies.green_all_action.call
|
54
|
+
|
55
|
+
redirect to("/")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
StoplightAdmin.loader!.eager_load
|
62
|
+
|
63
|
+
register StoplightAdmin::Application
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StoplightAdmin
|
4
|
+
class Dependencies
|
5
|
+
# @!attribute data_store
|
6
|
+
# @return [Stoplight::DataStore::Base]
|
7
|
+
attr_reader :data_store
|
8
|
+
private :data_store
|
9
|
+
|
10
|
+
# @param data_store [Stoplight::DataStore::Base]
|
11
|
+
def initialize(data_store:)
|
12
|
+
@data_store = data_store
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [StoplightAdmin::LightsRepository]
|
16
|
+
def lights_repository
|
17
|
+
StoplightAdmin::LightsRepository.new(data_store: data_store)
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [StoplightAdmin::Actions::Home]
|
21
|
+
def home_action
|
22
|
+
StoplightAdmin::Actions::Home.new(
|
23
|
+
lights_repository: lights_repository,
|
24
|
+
lights_stats: StoplightAdmin::LightsStats
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [StoplightAdmin::Actions::Stats]
|
29
|
+
def stats_action
|
30
|
+
StoplightAdmin::Actions::Stats.new(
|
31
|
+
lights_repository: lights_repository,
|
32
|
+
lights_stats: StoplightAdmin::LightsStats
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [StoplightAdmin::Actions::Lock]
|
37
|
+
def lock_action
|
38
|
+
StoplightAdmin::Actions::Lock.new(lights_repository: lights_repository)
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [StoplightAdmin::Actions::Unlock]
|
42
|
+
def unlock_action
|
43
|
+
StoplightAdmin::Actions::Unlock.new(lights_repository: lights_repository)
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return [StoplightAdmin::Actions::LockGreen]
|
47
|
+
def green_action
|
48
|
+
StoplightAdmin::Actions::LockGreen.new(lights_repository: lights_repository)
|
49
|
+
end
|
50
|
+
|
51
|
+
# @return [StoplightAdmin::Actions::LockRed]
|
52
|
+
def red_action
|
53
|
+
StoplightAdmin::Actions::LockRed.new(lights_repository: lights_repository)
|
54
|
+
end
|
55
|
+
|
56
|
+
# @return [StoplightAdmin::Actions::LockAllGreen]
|
57
|
+
def green_all_action
|
58
|
+
StoplightAdmin::Actions::LockAllGreen.new(lights_repository: lights_repository)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StoplightAdmin
|
4
|
+
class LightsRepository
|
5
|
+
class Light
|
6
|
+
COLORS = [
|
7
|
+
GREEN = Stoplight::Color::GREEN,
|
8
|
+
YELLOW = Stoplight::Color::YELLOW,
|
9
|
+
RED = Stoplight::Color::RED
|
10
|
+
].freeze
|
11
|
+
|
12
|
+
# @!attribute name
|
13
|
+
# @return [String]
|
14
|
+
attr_reader :name
|
15
|
+
|
16
|
+
# @!attribute color
|
17
|
+
# @return [String]
|
18
|
+
attr_reader :color
|
19
|
+
|
20
|
+
# @!attribute state
|
21
|
+
# @return [String]
|
22
|
+
attr_reader :state
|
23
|
+
|
24
|
+
# @!attribute failures
|
25
|
+
# @return [<Stoplight::Failure>]
|
26
|
+
attr_reader :failures
|
27
|
+
|
28
|
+
# @param name [String]
|
29
|
+
# @param color [String]
|
30
|
+
# @param state [String]
|
31
|
+
# @param failures [<Stoplight::Failure>]
|
32
|
+
def initialize(name:, color:, state:, failures:)
|
33
|
+
@name = name
|
34
|
+
@color = color
|
35
|
+
@state = state
|
36
|
+
@failures = failures
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [Boolean]
|
40
|
+
def locked?
|
41
|
+
!unlocked?
|
42
|
+
end
|
43
|
+
|
44
|
+
# @return [Boolean]
|
45
|
+
def unlocked?
|
46
|
+
state == Stoplight::State::UNLOCKED
|
47
|
+
end
|
48
|
+
|
49
|
+
def as_json
|
50
|
+
{
|
51
|
+
name: name,
|
52
|
+
color: color,
|
53
|
+
failures: failures,
|
54
|
+
locked: locked?
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
# @return [Array]
|
59
|
+
def default_sort_key
|
60
|
+
[-COLORS.index(color), name]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StoplightAdmin
|
4
|
+
class LightsRepository
|
5
|
+
# @!attribute data_store
|
6
|
+
# @return [Stoplight::DataStore::Base]
|
7
|
+
attr_reader :data_store
|
8
|
+
private :data_store
|
9
|
+
|
10
|
+
# @param data_store [Stoplight::DataStore::Base]
|
11
|
+
def initialize(data_store:)
|
12
|
+
@data_store = data_store
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [<StoplightAdmin::LightsRepository::Light>]
|
16
|
+
def all
|
17
|
+
data_store
|
18
|
+
.names
|
19
|
+
.map { |name| load_light(name) }
|
20
|
+
.sort_by(&:default_sort_key)
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param colors <String>] colors name
|
24
|
+
# @return [<StoplightAdmin::LightsRepository::Light>] lights with the requested colors
|
25
|
+
#
|
26
|
+
def with_color(*colors)
|
27
|
+
requested_colors = Array(colors)
|
28
|
+
|
29
|
+
all.select do |light|
|
30
|
+
requested_colors.include?(light.color)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# @param name [String] locks light by its name
|
35
|
+
# @param color [String, nil] locks to this color. When nil is given, locks to the current
|
36
|
+
# color
|
37
|
+
# @return [void]
|
38
|
+
def lock(name, color = nil)
|
39
|
+
light = build_light(name)
|
40
|
+
|
41
|
+
case color || light.color
|
42
|
+
when Stoplight::Color::GREEN
|
43
|
+
light.lock(Stoplight::Color::GREEN)
|
44
|
+
else
|
45
|
+
light.lock(Stoplight::Color::RED)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# @param name [String] unlocks light by its name
|
50
|
+
# @return [void]
|
51
|
+
def unlock(name)
|
52
|
+
build_light(name).unlock
|
53
|
+
end
|
54
|
+
|
55
|
+
private def load_light(name)
|
56
|
+
light = build_light(name)
|
57
|
+
failures, state = data_store.get_all(light)
|
58
|
+
|
59
|
+
Light.new(
|
60
|
+
name: name,
|
61
|
+
color: light.color,
|
62
|
+
state: state,
|
63
|
+
failures: failures
|
64
|
+
)
|
65
|
+
end
|
66
|
+
|
67
|
+
private def build_light(name)
|
68
|
+
Stoplight(name).with_data_store(data_store).build
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StoplightAdmin
|
4
|
+
class LightsStats
|
5
|
+
EMPTY_STATS = {
|
6
|
+
count_red: 0, count_yellow: 0, count_green: 0,
|
7
|
+
percent_red: 0, percent_yellow: 0, percent_green: 0
|
8
|
+
}.freeze
|
9
|
+
|
10
|
+
# @!attribute lights
|
11
|
+
# @return [<StoplightAdmin::LightsRepository::Light>]
|
12
|
+
attr_reader :lights
|
13
|
+
private :lights
|
14
|
+
|
15
|
+
class << self
|
16
|
+
def call(lights)
|
17
|
+
new(lights).call
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# @param lights [<StoplightAdmin::LightsRepository::Light>]
|
22
|
+
def initialize(lights)
|
23
|
+
@lights = lights
|
24
|
+
end
|
25
|
+
|
26
|
+
def call
|
27
|
+
return EMPTY_STATS if size.zero?
|
28
|
+
|
29
|
+
EMPTY_STATS.merge(
|
30
|
+
count_red: count_red,
|
31
|
+
count_yellow: count_yellow,
|
32
|
+
count_green: count_green,
|
33
|
+
percent_red: percent_red,
|
34
|
+
percent_yellow: percent_yellow,
|
35
|
+
percent_green: percent_green
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
private def count_red
|
40
|
+
count_lights(RED)
|
41
|
+
end
|
42
|
+
|
43
|
+
private def percent_red
|
44
|
+
percent_lights(RED)
|
45
|
+
end
|
46
|
+
|
47
|
+
private def count_green
|
48
|
+
count_lights(GREEN)
|
49
|
+
end
|
50
|
+
|
51
|
+
private def percent_green
|
52
|
+
percent_lights(GREEN)
|
53
|
+
end
|
54
|
+
|
55
|
+
private def count_yellow
|
56
|
+
count_lights(YELLOW)
|
57
|
+
end
|
58
|
+
|
59
|
+
private def percent_yellow
|
60
|
+
percent_lights(YELLOW)
|
61
|
+
end
|
62
|
+
|
63
|
+
private def count_lights(color)
|
64
|
+
lights.count { |l| l.color == color }
|
65
|
+
end
|
66
|
+
|
67
|
+
private def percent_lights(color)
|
68
|
+
(100 * count_lights(color).fdiv(size)).ceil
|
69
|
+
end
|
70
|
+
|
71
|
+
private def size
|
72
|
+
lights.size
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -50,23 +50,23 @@
|
|
50
50
|
<% lights.each do |l| %>
|
51
51
|
<tr>
|
52
52
|
<td class="indicator">
|
53
|
-
<% if l
|
53
|
+
<% if l.color == GREEN %>
|
54
54
|
<form method="post" action="<%= url('/red') %>">
|
55
|
-
<input type="hidden" name="names" value="<%= ERB::Util.html_escape(l
|
55
|
+
<input type="hidden" name="names" value="<%= ERB::Util.html_escape(l.name) %>">
|
56
56
|
<button type="submit" class="btn btn-success">
|
57
57
|
<span>G</span><span class="hidden-xs">REEN</span>
|
58
58
|
</button>
|
59
59
|
</form>
|
60
|
-
<% elsif l
|
60
|
+
<% elsif l.color == YELLOW %>
|
61
61
|
<form method="post" action="<%= url('/green') %>">
|
62
|
-
<input type="hidden" name="names" value="<%= ERB::Util.html_escape(l
|
62
|
+
<input type="hidden" name="names" value="<%= ERB::Util.html_escape(l.name) %>">
|
63
63
|
<button type="submit" class="btn btn-warning">
|
64
64
|
<span>Y</span><span class="hidden-xs">ELLOW</span>
|
65
65
|
</button>
|
66
66
|
</form>
|
67
67
|
<% else %>
|
68
68
|
<form method="post" action="<%= url('/green') %>">
|
69
|
-
<input type="hidden" name="names" value="<%= ERB::Util.html_escape(l
|
69
|
+
<input type="hidden" name="names" value="<%= ERB::Util.html_escape(l.name) %>">
|
70
70
|
<button type="submit" class="btn btn-danger">
|
71
71
|
<span>R</span><span class="hidden-xs">ED</span>
|
72
72
|
</button>
|
@@ -74,16 +74,16 @@
|
|
74
74
|
<% end %>
|
75
75
|
</td>
|
76
76
|
<td class="locked">
|
77
|
-
<% if l
|
77
|
+
<% if l.locked? %>
|
78
78
|
<form method="post" action="<%= url('/unlock') %>">
|
79
|
-
<input type="hidden" name="names" value="<%= ERB::Util.html_escape(l
|
79
|
+
<input type="hidden" name="names" value="<%= ERB::Util.html_escape(l.name) %>">
|
80
80
|
<button type="submit" class="btn btn-link">
|
81
81
|
<span class="glyphicon glyphicon-lock"></span>
|
82
82
|
</button>
|
83
83
|
</form>
|
84
84
|
<% else %>
|
85
85
|
<form method="post" action="<%= url('/lock') %>">
|
86
|
-
<input type="hidden" name="names" value="<%= ERB::Util.html_escape(l
|
86
|
+
<input type="hidden" name="names" value="<%= ERB::Util.html_escape(l.name) %>">
|
87
87
|
<button type="submit" class="btn btn-link">
|
88
88
|
<span class="glyphicon glyphicon-minus"></span>
|
89
89
|
</button>
|
@@ -91,12 +91,12 @@
|
|
91
91
|
<% end %>
|
92
92
|
</td>
|
93
93
|
<td class="name">
|
94
|
-
<%= l
|
94
|
+
<%= l.name %>
|
95
95
|
</td>
|
96
96
|
<td class="failures">
|
97
|
-
<% if l
|
97
|
+
<% if l.failures %>
|
98
98
|
<ul>
|
99
|
-
<% l
|
99
|
+
<% l.failures.each do |failure| %>
|
100
100
|
<li>
|
101
101
|
<span class="error">
|
102
102
|
<%= ERB::Util.html_escape(failure.error_class) %>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "stoplight"
|
2
|
+
require "zeitwerk"
|
3
|
+
|
4
|
+
module StoplightAdmin
|
5
|
+
COLORS = [
|
6
|
+
GREEN = Stoplight::Color::GREEN,
|
7
|
+
YELLOW = Stoplight::Color::YELLOW,
|
8
|
+
RED = Stoplight::Color::RED
|
9
|
+
].freeze
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def loader!
|
13
|
+
@loader ||= Zeitwerk::Loader.for_gem.tap do |loader|
|
14
|
+
loader.ignore("#{__dir__}/sinatra")
|
15
|
+
loader.setup
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
StoplightAdmin.loader!
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stoplight-admin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.0
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cameron Desautels
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2023-
|
12
|
+
date: 2023-08-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: redis
|
@@ -25,34 +25,54 @@ dependencies:
|
|
25
25
|
- - ">="
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: '3.2'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: zeitwerk
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
28
42
|
- !ruby/object:Gem::Dependency
|
29
43
|
name: sinatra-contrib
|
30
44
|
requirement: !ruby/object:Gem::Requirement
|
31
45
|
requirements:
|
32
|
-
- - "
|
46
|
+
- - ">="
|
33
47
|
- !ruby/object:Gem::Version
|
34
48
|
version: 2.2.3
|
49
|
+
- - "<"
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: 3.2.0
|
35
52
|
type: :runtime
|
36
53
|
prerelease: false
|
37
54
|
version_requirements: !ruby/object:Gem::Requirement
|
38
55
|
requirements:
|
39
|
-
- - "
|
56
|
+
- - ">="
|
40
57
|
- !ruby/object:Gem::Version
|
41
58
|
version: 2.2.3
|
59
|
+
- - "<"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 3.2.0
|
42
62
|
- !ruby/object:Gem::Dependency
|
43
63
|
name: stoplight
|
44
64
|
requirement: !ruby/object:Gem::Requirement
|
45
65
|
requirements:
|
46
66
|
- - ">="
|
47
67
|
- !ruby/object:Gem::Version
|
48
|
-
version: '1
|
68
|
+
version: '4.1'
|
49
69
|
type: :runtime
|
50
70
|
prerelease: false
|
51
71
|
version_requirements: !ruby/object:Gem::Requirement
|
52
72
|
requirements:
|
53
73
|
- - ">="
|
54
74
|
- !ruby/object:Gem::Version
|
55
|
-
version: '1
|
75
|
+
version: '4.1'
|
56
76
|
description: A simple administration interface for Stoplight.
|
57
77
|
email:
|
58
78
|
- camdez@gmail.com
|
@@ -64,8 +84,23 @@ files:
|
|
64
84
|
- LICENSE.md
|
65
85
|
- README.md
|
66
86
|
- lib/sinatra/stoplight_admin.rb
|
67
|
-
- lib/
|
68
|
-
- lib/
|
87
|
+
- lib/stoplight_admin.rb
|
88
|
+
- lib/stoplight_admin/actions/action.rb
|
89
|
+
- lib/stoplight_admin/actions/home.rb
|
90
|
+
- lib/stoplight_admin/actions/lock.rb
|
91
|
+
- lib/stoplight_admin/actions/lock_all_green.rb
|
92
|
+
- lib/stoplight_admin/actions/lock_green.rb
|
93
|
+
- lib/stoplight_admin/actions/lock_red.rb
|
94
|
+
- lib/stoplight_admin/actions/stats.rb
|
95
|
+
- lib/stoplight_admin/actions/unlock.rb
|
96
|
+
- lib/stoplight_admin/application.rb
|
97
|
+
- lib/stoplight_admin/application/helpers.rb
|
98
|
+
- lib/stoplight_admin/dependencies.rb
|
99
|
+
- lib/stoplight_admin/lights_repository.rb
|
100
|
+
- lib/stoplight_admin/lights_repository/light.rb
|
101
|
+
- lib/stoplight_admin/lights_stats.rb
|
102
|
+
- lib/stoplight_admin/views/index.erb
|
103
|
+
- lib/stoplight_admin/views/layout.erb
|
69
104
|
homepage: https://github.com/bolshakov/stoplight-admin
|
70
105
|
licenses:
|
71
106
|
- MIT
|
@@ -78,14 +113,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
78
113
|
requirements:
|
79
114
|
- - ">="
|
80
115
|
- !ruby/object:Gem::Version
|
81
|
-
version:
|
116
|
+
version: 3.0.0
|
82
117
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
118
|
requirements:
|
84
|
-
- - "
|
119
|
+
- - ">="
|
85
120
|
- !ruby/object:Gem::Version
|
86
|
-
version:
|
121
|
+
version: '0'
|
87
122
|
requirements: []
|
88
|
-
rubygems_version: 3.4.
|
123
|
+
rubygems_version: 3.4.18
|
89
124
|
signing_key:
|
90
125
|
specification_version: 4
|
91
126
|
summary: A simple administration interface for Stoplight.
|
File without changes
|