startback-websocket 0.14.0 → 0.14.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -2
  3. data/README.md +64 -9
  4. data/lib/startback/ext/context.rb +6 -0
  5. data/lib/startback/ext.rb +1 -2
  6. data/lib/startback/websocket/app.rb +82 -0
  7. data/lib/startback/websocket/hub/app.rb +28 -0
  8. data/lib/startback/websocket/hub/builder.rb +55 -0
  9. data/lib/startback/websocket/hub/errors.rb +9 -0
  10. data/lib/startback/websocket/hub/message.rb +29 -0
  11. data/lib/startback/websocket/hub/middleware/command_handler.rb +34 -0
  12. data/lib/startback/websocket/hub/middleware/room_handler.rb +30 -0
  13. data/lib/startback/websocket/hub/middleware.rb +12 -0
  14. data/lib/startback/websocket/hub/participant.rb +16 -0
  15. data/lib/startback/websocket/hub/room.rb +46 -0
  16. data/lib/startback/websocket/hub.rb +15 -0
  17. data/lib/startback/websocket.rb +8 -0
  18. data/spec/spec_helper.rb +21 -32
  19. data/spec/unit/hub/test_builder.rb +141 -0
  20. data/spec/unit/hub/test_room.rb +27 -0
  21. data/spec/unit/test_app.rb +35 -0
  22. data/tasks/test.rake +0 -1
  23. metadata +20 -91
  24. data/lib/startback/audit/prometheus.rb +0 -87
  25. data/lib/startback/audit/shared.rb +0 -17
  26. data/lib/startback/audit/trailer.rb +0 -129
  27. data/lib/startback/audit.rb +0 -3
  28. data/lib/startback/caching/entity_cache.rb +0 -157
  29. data/lib/startback/caching/no_store.rb +0 -28
  30. data/lib/startback/caching/store.rb +0 -34
  31. data/lib/startback/context/h_factory.rb +0 -43
  32. data/lib/startback/context/middleware.rb +0 -53
  33. data/lib/startback/context.rb +0 -122
  34. data/lib/startback/errors.rb +0 -197
  35. data/lib/startback/event/agent.rb +0 -84
  36. data/lib/startback/event/bus/bunny/async.rb +0 -162
  37. data/lib/startback/event/bus/bunny.rb +0 -1
  38. data/lib/startback/event/bus/memory/async.rb +0 -45
  39. data/lib/startback/event/bus/memory/sync.rb +0 -35
  40. data/lib/startback/event/bus/memory.rb +0 -2
  41. data/lib/startback/event/bus.rb +0 -100
  42. data/lib/startback/event/engine.rb +0 -94
  43. data/lib/startback/event/ext/context.rb +0 -5
  44. data/lib/startback/event/ext/operation.rb +0 -13
  45. data/lib/startback/event.rb +0 -47
  46. data/lib/startback/ext/date_time.rb +0 -9
  47. data/lib/startback/ext/time.rb +0 -9
  48. data/lib/startback/model.rb +0 -6
  49. data/lib/startback/operation/error_operation.rb +0 -19
  50. data/lib/startback/operation/multi_operation.rb +0 -28
  51. data/lib/startback/operation.rb +0 -78
  52. data/lib/startback/services.rb +0 -11
  53. data/lib/startback/support/data_object.rb +0 -71
  54. data/lib/startback/support/env.rb +0 -41
  55. data/lib/startback/support/fake_logger.rb +0 -18
  56. data/lib/startback/support/hooks.rb +0 -48
  57. data/lib/startback/support/log_formatter.rb +0 -34
  58. data/lib/startback/support/logger.rb +0 -34
  59. data/lib/startback/support/operation_runner.rb +0 -150
  60. data/lib/startback/support/robustness.rb +0 -157
  61. data/lib/startback/support/transaction_manager.rb +0 -25
  62. data/lib/startback/support/transaction_policy.rb +0 -33
  63. data/lib/startback/support/world.rb +0 -54
  64. data/lib/startback/support.rb +0 -26
  65. data/lib/startback/version.rb +0 -8
  66. data/lib/startback/web/api.rb +0 -99
  67. data/lib/startback/web/auto_caching.rb +0 -85
  68. data/lib/startback/web/catch_all.rb +0 -52
  69. data/lib/startback/web/cors_headers.rb +0 -80
  70. data/lib/startback/web/health_check.rb +0 -49
  71. data/lib/startback/web/magic_assets/ng_html_transformer.rb +0 -80
  72. data/lib/startback/web/magic_assets/rake_tasks.rb +0 -64
  73. data/lib/startback/web/magic_assets.rb +0 -98
  74. data/lib/startback/web/middleware.rb +0 -13
  75. data/lib/startback/web/prometheus.rb +0 -16
  76. data/lib/startback/web/shield.rb +0 -58
  77. data/lib/startback.rb +0 -43
  78. data/spec/unit/audit/test_prometheus.rb +0 -72
  79. data/spec/unit/audit/test_trailer.rb +0 -105
  80. data/spec/unit/caching/test_entity_cache.rb +0 -136
  81. data/spec/unit/context/test_abstraction_factory.rb +0 -64
  82. data/spec/unit/context/test_dup.rb +0 -42
  83. data/spec/unit/context/test_fork.rb +0 -37
  84. data/spec/unit/context/test_h_factory.rb +0 -31
  85. data/spec/unit/context/test_middleware.rb +0 -45
  86. data/spec/unit/context/test_with_world.rb +0 -20
  87. data/spec/unit/context/test_world.rb +0 -17
  88. data/spec/unit/event/bus/memory/test_async.rb +0 -43
  89. data/spec/unit/event/bus/memory/test_sync.rb +0 -43
  90. data/spec/unit/support/hooks/test_after_hook.rb +0 -54
  91. data/spec/unit/support/hooks/test_before_hook.rb +0 -54
  92. data/spec/unit/support/operation_runner/test_around_run.rb +0 -156
  93. data/spec/unit/support/operation_runner/test_before_after_call.rb +0 -48
  94. data/spec/unit/support/test_data_object.rb +0 -156
  95. data/spec/unit/support/test_env.rb +0 -75
  96. data/spec/unit/support/test_robusteness.rb +0 -229
  97. data/spec/unit/support/test_transaction_manager.rb +0 -64
  98. data/spec/unit/support/test_world.rb +0 -72
  99. data/spec/unit/test_event.rb +0 -62
  100. data/spec/unit/test_operation.rb +0 -55
  101. data/spec/unit/test_support.rb +0 -40
  102. data/spec/unit/web/fixtures/assets/app/hello.es6 +0 -4
  103. data/spec/unit/web/fixtures/assets/app/hello.html +0 -1
  104. data/spec/unit/web/fixtures/assets/index.es6 +0 -1
  105. data/spec/unit/web/test_api.rb +0 -82
  106. data/spec/unit/web/test_auto_caching.rb +0 -81
  107. data/spec/unit/web/test_catch_all.rb +0 -77
  108. data/spec/unit/web/test_cors_headers.rb +0 -88
  109. data/spec/unit/web/test_healthcheck.rb +0 -59
  110. data/spec/unit/web/test_magic_assets.rb +0 -82
@@ -0,0 +1,141 @@
1
+ require 'spec_helper'
2
+ require 'startback/websocket'
3
+
4
+ module Startback
5
+ module Websocket
6
+ module Hub
7
+ describe Builder do
8
+
9
+ class TestMiddleware
10
+
11
+ def initialize(app, opts = {})
12
+ @app = app
13
+ @@init = true
14
+ @@opts = opts
15
+ end
16
+
17
+ def call(data, socket)
18
+ @@calls ||= []
19
+ @@calls << data
20
+ @app.call(data, socket)
21
+ end
22
+
23
+ def self.reset
24
+ @@init = false
25
+ @@calls = []
26
+ end
27
+
28
+ def self.init
29
+ @@init
30
+ end
31
+
32
+ def self.calls
33
+ @@calls
34
+ end
35
+
36
+ def self.opts
37
+ @@opts
38
+ end
39
+ end
40
+
41
+ before(:each) do
42
+ TestMiddleware.reset
43
+ end
44
+
45
+ it 'calls the block given at initialization' do
46
+ called = false
47
+ Builder.new(SpecHelpers::SubContext.new) { called = true }
48
+ expect(called).to eql(true)
49
+ end
50
+
51
+ context 'to_websocket_app' do
52
+ it 'returns an App instance' do
53
+ builder = Builder.new(SpecHelpers::SubContext.new) do
54
+ end
55
+ app = builder.to_websocket_app
56
+ expect(app).to be_a(App)
57
+ end
58
+ end
59
+
60
+ context 'to_handler' do
61
+
62
+ it 'instantiates middleware classes' do
63
+ handler = Builder.new(SpecHelpers::SubContext.new) do
64
+ use TestMiddleware
65
+ end.to_handler
66
+
67
+ expect(TestMiddleware.init).to eql(true)
68
+ end
69
+
70
+ it 'allows options to be passed to middleware classes' do
71
+ handler = Builder.new(SpecHelpers::SubContext.new) do
72
+ use TestMiddleware, { some: 'option' }
73
+ end.to_handler
74
+
75
+ expect(TestMiddleware.init).to eql(true)
76
+ expect(TestMiddleware.opts).to eql({ some: 'option' })
77
+ end
78
+
79
+ it 'creates the correct chain of handlers' do
80
+ handler = Builder.new(SpecHelpers::SubContext.new) do
81
+ use TestMiddleware
82
+ use TestMiddleware
83
+ end.to_handler
84
+
85
+ handler.call({test: 42}, SpecHelpers::MockSocket.new)
86
+ expect(TestMiddleware.calls.size).to eql(2)
87
+ end
88
+
89
+ it 'supports commands' do
90
+ handler = Builder.new(SpecHelpers::SubContext.new) do
91
+ command :hello do |command, socket|
92
+ socket.send('from hello')
93
+ end
94
+
95
+ command :foo do |command, socket|
96
+ socket.send('from foo')
97
+ end
98
+ end.to_handler
99
+
100
+ socket = SpecHelpers::MockSocket.new
101
+
102
+ msg = SpecHelpers::MockFayeEvent.new({ :headers => { :command => 'hello' } })
103
+ handler.call(msg, socket, {})
104
+ expect(socket.last_message).to eql('from hello')
105
+
106
+ msg = SpecHelpers::MockFayeEvent.new({ :headers => { :command => 'foo' } })
107
+ handler.call(msg, socket, {})
108
+ expect(socket.last_message).to eql('from foo')
109
+ end
110
+
111
+ it 'supports rooms' do
112
+ handler = Builder.new(SpecHelpers::SubContext.new) do
113
+ room 'a' do |room|
114
+ command :hello do |command, socket|
115
+ socket.send("hi from #{room.name}!")
116
+ end
117
+ end
118
+
119
+ room 'b' do
120
+ command :hello do |command, socket|
121
+ socket.send(command)
122
+ end
123
+ end
124
+ end.to_handler
125
+
126
+ socket = SpecHelpers::MockSocket.new
127
+
128
+ msg = SpecHelpers::MockFayeEvent.new({ :headers => { command: 'hello', room: 'a' } })
129
+ handler.call(msg, socket, {})
130
+ expect(socket.last_message).to eql('hi from a!')
131
+
132
+ msg = SpecHelpers::MockFayeEvent.new({ :headers => { command: 'hello', room: 'b' } })
133
+ handler.call(msg, socket, {})
134
+ expect(socket.last_message).to eql(msg)
135
+ end
136
+
137
+ end
138
+ end # describe
139
+ end # module Hub
140
+ end # module Websocket
141
+ end # module Startback
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+ require 'startback/websocket'
3
+
4
+ module Startback
5
+ module Websocket
6
+ module Hub
7
+ describe Room do
8
+
9
+ context 'add' do
10
+
11
+ room = Room.new('room-name')
12
+
13
+ it 'expects participant instances' do
14
+ expect { room.add SpecHelpers.MockSocket.new }.to raise_error
15
+ end
16
+
17
+ it 'allows adding participants to the room' do
18
+ room.add Participant.new(SpecHelpers::MockSocket.new, SpecHelpers::SubContext.new)
19
+ expect(room.participants.size).to eql(1)
20
+ end
21
+
22
+ end
23
+
24
+ end # describe
25
+ end # module Hub
26
+ end # module Websocket
27
+ end # module Startback
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+ require 'startback/websocket'
3
+
4
+ module Startback
5
+ module Websocket
6
+ describe App do
7
+ include Rack::Test::Methods
8
+
9
+ def app
10
+ App.new(SpecHelpers::SubContext.new)
11
+ end
12
+
13
+ it 'returns a 400 when not used with proper websocket handshake' do
14
+ get '/'
15
+ expect(last_response.status).to eql(400)
16
+ expect(last_response.body).to eql('Websocket only!')
17
+ end
18
+
19
+ it 'does respond with proper handshake in the context of websocket connections' do
20
+ header 'connection', 'upgrade'
21
+ header 'upgrade', 'websocket'
22
+ get '/'
23
+ # https://github.com/faye/faye-websocket-ruby/blob/main/lib/faye/websocket.rb#L93
24
+ expect(last_response.status).to eql(-1)
25
+ end
26
+
27
+ it 'serves the javascript client properly' do
28
+ get '/client.js'
29
+ expect(last_response.status).to eql(200)
30
+ expect(last_response['Content-Type']).to eql('application/javascript')
31
+ end
32
+
33
+ end # describe
34
+ end # module Web
35
+ end # module Startback
data/tasks/test.rake CHANGED
@@ -1,5 +1,4 @@
1
1
  require 'rspec/core/rake_task'
2
- require 'path'
3
2
 
4
3
  namespace :test do
5
4
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: startback-websocket
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.0
4
+ version: 0.14.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bernard Lambeau
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-06-07 00:00:00.000000000 Z
11
+ date: 2022-06-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: startback
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 0.14.0
19
+ version: 0.14.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 0.14.0
26
+ version: 0.14.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: faye-websocket
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -121,95 +121,24 @@ files:
121
121
  - Gemfile
122
122
  - README.md
123
123
  - Rakefile
124
- - lib/startback.rb
125
- - lib/startback/audit.rb
126
- - lib/startback/audit/prometheus.rb
127
- - lib/startback/audit/shared.rb
128
- - lib/startback/audit/trailer.rb
129
- - lib/startback/caching/entity_cache.rb
130
- - lib/startback/caching/no_store.rb
131
- - lib/startback/caching/store.rb
132
- - lib/startback/context.rb
133
- - lib/startback/context/h_factory.rb
134
- - lib/startback/context/middleware.rb
135
- - lib/startback/errors.rb
136
- - lib/startback/event.rb
137
- - lib/startback/event/agent.rb
138
- - lib/startback/event/bus.rb
139
- - lib/startback/event/bus/bunny.rb
140
- - lib/startback/event/bus/bunny/async.rb
141
- - lib/startback/event/bus/memory.rb
142
- - lib/startback/event/bus/memory/async.rb
143
- - lib/startback/event/bus/memory/sync.rb
144
- - lib/startback/event/engine.rb
145
- - lib/startback/event/ext/context.rb
146
- - lib/startback/event/ext/operation.rb
147
124
  - lib/startback/ext.rb
148
- - lib/startback/ext/date_time.rb
149
- - lib/startback/ext/time.rb
150
- - lib/startback/model.rb
151
- - lib/startback/operation.rb
152
- - lib/startback/operation/error_operation.rb
153
- - lib/startback/operation/multi_operation.rb
154
- - lib/startback/services.rb
155
- - lib/startback/support.rb
156
- - lib/startback/support/data_object.rb
157
- - lib/startback/support/env.rb
158
- - lib/startback/support/fake_logger.rb
159
- - lib/startback/support/hooks.rb
160
- - lib/startback/support/log_formatter.rb
161
- - lib/startback/support/logger.rb
162
- - lib/startback/support/operation_runner.rb
163
- - lib/startback/support/robustness.rb
164
- - lib/startback/support/transaction_manager.rb
165
- - lib/startback/support/transaction_policy.rb
166
- - lib/startback/support/world.rb
167
- - lib/startback/version.rb
168
- - lib/startback/web/api.rb
169
- - lib/startback/web/auto_caching.rb
170
- - lib/startback/web/catch_all.rb
171
- - lib/startback/web/cors_headers.rb
172
- - lib/startback/web/health_check.rb
173
- - lib/startback/web/magic_assets.rb
174
- - lib/startback/web/magic_assets/ng_html_transformer.rb
175
- - lib/startback/web/magic_assets/rake_tasks.rb
176
- - lib/startback/web/middleware.rb
177
- - lib/startback/web/prometheus.rb
178
- - lib/startback/web/shield.rb
125
+ - lib/startback/ext/context.rb
126
+ - lib/startback/websocket.rb
127
+ - lib/startback/websocket/app.rb
128
+ - lib/startback/websocket/hub.rb
129
+ - lib/startback/websocket/hub/app.rb
130
+ - lib/startback/websocket/hub/builder.rb
131
+ - lib/startback/websocket/hub/errors.rb
132
+ - lib/startback/websocket/hub/message.rb
133
+ - lib/startback/websocket/hub/middleware.rb
134
+ - lib/startback/websocket/hub/middleware/command_handler.rb
135
+ - lib/startback/websocket/hub/middleware/room_handler.rb
136
+ - lib/startback/websocket/hub/participant.rb
137
+ - lib/startback/websocket/hub/room.rb
179
138
  - spec/spec_helper.rb
180
- - spec/unit/audit/test_prometheus.rb
181
- - spec/unit/audit/test_trailer.rb
182
- - spec/unit/caching/test_entity_cache.rb
183
- - spec/unit/context/test_abstraction_factory.rb
184
- - spec/unit/context/test_dup.rb
185
- - spec/unit/context/test_fork.rb
186
- - spec/unit/context/test_h_factory.rb
187
- - spec/unit/context/test_middleware.rb
188
- - spec/unit/context/test_with_world.rb
189
- - spec/unit/context/test_world.rb
190
- - spec/unit/event/bus/memory/test_async.rb
191
- - spec/unit/event/bus/memory/test_sync.rb
192
- - spec/unit/support/hooks/test_after_hook.rb
193
- - spec/unit/support/hooks/test_before_hook.rb
194
- - spec/unit/support/operation_runner/test_around_run.rb
195
- - spec/unit/support/operation_runner/test_before_after_call.rb
196
- - spec/unit/support/test_data_object.rb
197
- - spec/unit/support/test_env.rb
198
- - spec/unit/support/test_robusteness.rb
199
- - spec/unit/support/test_transaction_manager.rb
200
- - spec/unit/support/test_world.rb
201
- - spec/unit/test_event.rb
202
- - spec/unit/test_operation.rb
203
- - spec/unit/test_support.rb
204
- - spec/unit/web/fixtures/assets/app/hello.es6
205
- - spec/unit/web/fixtures/assets/app/hello.html
206
- - spec/unit/web/fixtures/assets/index.es6
207
- - spec/unit/web/test_api.rb
208
- - spec/unit/web/test_auto_caching.rb
209
- - spec/unit/web/test_catch_all.rb
210
- - spec/unit/web/test_cors_headers.rb
211
- - spec/unit/web/test_healthcheck.rb
212
- - spec/unit/web/test_magic_assets.rb
139
+ - spec/unit/hub/test_builder.rb
140
+ - spec/unit/hub/test_room.rb
141
+ - spec/unit/test_app.rb
213
142
  - tasks/test.rake
214
143
  homepage: https://www.enspirit.be
215
144
  licenses:
@@ -1,87 +0,0 @@
1
- require_relative 'shared'
2
- require 'prometheus/client'
3
-
4
- module Startback
5
- module Audit
6
- #
7
- # Prometheus exporter abstraction, that can be registered as an around
8
- # hook on OperationRunner and as a prometheus client on Context instances.
9
- #
10
- # The exporter uses the ruby client for prometheus to expose metrics regarding Operation runs.
11
- #
12
- # The following metrics are exported:
13
- #
14
- # A counter 'operation_errors' (failed runs)
15
- # A histogram 'operation_calls'
16
- #
17
- # All these metrics use the following labels
18
- # - operation : class name of the operation executed
19
- #
20
- # Given that this Exporter is intended to be used as around hook on an
21
- # `OperationRunner`, operations that fail at construction time will not be
22
- # exported at all, since they can't be ran in the first place. This may lead
23
- # to metrics not containing important errors cases if operations check their
24
- # input at construction time.
25
- #
26
- class Prometheus
27
- include Shared
28
-
29
- def initialize(options = {})
30
- @prefix = options[:prefix] || "startback"
31
- @options = options
32
- @registry = ::Prometheus::Client.registry
33
- all_labels = [:operation, :startback_version] + option_labels.keys
34
- @errors = @registry.counter(
35
- :"#{prefix}_operation_errors",
36
- docstring: 'A counter of operation errors',
37
- labels: all_labels)
38
- @calls = @registry.histogram(
39
- :"#{prefix}_operation_calls",
40
- docstring: 'A histogram of operation latency',
41
- labels: all_labels)
42
- end
43
- attr_reader :registry, :calls, :errors, :options, :prefix
44
-
45
- def call(runner, op)
46
- name = op_name(op)
47
- result = nil
48
- time = Benchmark.realtime{
49
- result = yield
50
- }
51
- ignore_safely {
52
- @calls.observe(time, labels: get_labels(name))
53
- }
54
- result
55
- rescue => ex
56
- ignore_safely {
57
- @errors.increment(labels: get_labels(name))
58
- }
59
- raise
60
- end
61
-
62
- protected
63
-
64
- def ignore_safely
65
- yield
66
- rescue => ex
67
- nil
68
- end
69
-
70
- def get_labels(op_name)
71
- option_labels.merge({
72
- operation: op_name,
73
- startback_version: version
74
- })
75
- end
76
-
77
- def option_labels
78
- @options[:labels] || {}
79
- end
80
-
81
- def version
82
- Startback::VERSION
83
- end
84
-
85
- end # class Prometheus
86
- end # module Audit
87
- end # module Startback
@@ -1,17 +0,0 @@
1
- module Startback
2
- module Audit
3
- module Shared
4
-
5
- def op_name(op)
6
- return op.op_name if op.respond_to?(:op_name)
7
-
8
- case op
9
- when String then op
10
- when Class then op.name
11
- else op.class.name
12
- end
13
- end
14
-
15
- end # module Shared
16
- end # module Audit
17
- end # module Startback
@@ -1,129 +0,0 @@
1
- require_relative 'shared'
2
- require 'forwardable'
3
- module Startback
4
- module Audit
5
- #
6
- # Log & Audit trail abstraction, that can be registered as an around
7
- # hook on OperationRunner and as an actual logger on Context instances.
8
- #
9
- # The trail is outputted as JSON lines, using a Logger on the "device"
10
- # passed at construction. The following JSON entries are dumped:
11
- #
12
- # - severity : INFO or ERROR
13
- # - time : ISO8601 Datetime of operation execution
14
- # - op : class name of the operation executed
15
- # - op_took : Execution duration of the operation
16
- # - op_data : Dump of operation input data
17
- # - context : Execution context, through its `h` information contract (IC)
18
- #
19
- # Dumping of operation data follows the following duck typing conventions:
20
- #
21
- # - If the operation instance responds to `to_trail`, this data is taken
22
- # - If the operation instance responds to `input`, this data is taken
23
- # - If the operation instance responds to `request`, this data is taken
24
- # - Otherwise op_data is a JSON null
25
- #
26
- # By contributing to the Context's `h` IC, users can easily dump information that
27
- # makes sense (such as the operation execution requester).
28
- #
29
- # The class implements a sanitization process when dumping the context and
30
- # operation data. Blacklisted words taken in construction options are used to
31
- # prevent dumping hash keys that match them (insentively). Default stop words
32
- # are equivalent to:
33
- #
34
- # Trailer.new("/var/log/trail.log", {
35
- # blacklist: "token password secret credential"
36
- # })
37
- #
38
- # Please note that the sanitization process does not apply recursively if
39
- # the operation data is hierarchic. It only applies to the top object of
40
- # Hash and [Hash]. Use `Operation#to_trail` to fine-tune your audit trail.
41
- #
42
- # Given that this Trailer is intended to be used as around hook on an
43
- # `OperationRunner`, operations that fail at construction time will not be
44
- # trailed at all, since they can't be ran in the first place. This may lead
45
- # to trails not containing important errors cases if operations check their
46
- # input at construction time.
47
- #
48
- class Trailer
49
- include Shared
50
- extend Forwardable
51
- def_delegators :@logger, :debug, :info, :warn, :error, :fatal
52
-
53
- DEFAULT_OPTIONS = {
54
-
55
- # Words used to stop dumping for, e.g., security reasons
56
- blacklist: "token password secret credential"
57
-
58
- }
59
-
60
- def initialize(device, options = {})
61
- @options = DEFAULT_OPTIONS.merge(options)
62
- @logger = ::Logger.new(device, 'daily')
63
- @logger.formatter = Support::LogFormatter.new
64
- end
65
- attr_reader :logger, :options
66
-
67
- def call(runner, op)
68
- result = nil
69
- time = Benchmark.realtime{ result = yield }
70
- logger.info(op_to_trail(op, time))
71
- result
72
- rescue => ex
73
- logger.error(op_to_trail(op, time, ex))
74
- raise
75
- end
76
-
77
- protected
78
-
79
- def op_to_trail(op, time = nil, ex = nil)
80
- log_msg = {
81
- op_took: time ? time.round(8) : nil,
82
- op: op_name(op),
83
- context: op_context(op),
84
- op_data: op_data(op)
85
- }.compact
86
- log_msg[:error] = ex if ex
87
- log_msg
88
- end
89
-
90
- def op_context(op)
91
- sanitize(op.respond_to?(:context, false) ? op.context.to_h : {})
92
- end
93
-
94
- def op_data(op)
95
- data = if op.respond_to?(:op_data, false)
96
- op.op_data
97
- elsif op.respond_to?(:to_trail, false)
98
- op.to_trail
99
- elsif op.respond_to?(:input, false)
100
- op.input
101
- elsif op.respond_to?(:request, false)
102
- op.request
103
- elsif op.is_a?(Operation::MultiOperation)
104
- op.ops.map{ |sub_op| op_to_trail(sub_op) }
105
- end
106
- sanitize(data)
107
- end
108
-
109
- def sanitize(data)
110
- case data
111
- when Hash, OpenStruct
112
- data.dup.delete_if{|k| k.to_s =~ blacklist_rx }
113
- when Enumerable
114
- data.map{|elm| sanitize(elm) }.compact
115
- else
116
- data
117
- end
118
- end
119
-
120
- def blacklist_rx
121
- @blacklist_rx ||= Regexp.new(
122
- options[:blacklist].split(/\s+/).join("|"),
123
- Regexp::IGNORECASE
124
- )
125
- end
126
-
127
- end # class Trailer
128
- end # module Audit
129
- end # module Startback
@@ -1,3 +0,0 @@
1
- require_relative 'audit/shared'
2
- require_relative 'audit/trailer'
3
- require_relative 'audit/prometheus'