startback-jobs 0.14.0 → 0.14.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +3 -2
- data/lib/startback/ext/support/operation_runner.rb +17 -0
- data/lib/startback/ext/web/api.rb +12 -0
- data/lib/startback/ext.rb +2 -2
- data/lib/startback/jobs/agent.rb +15 -0
- data/lib/startback/jobs/api.rb +12 -0
- data/lib/startback/jobs/event/job_created.rb +8 -0
- data/lib/startback/jobs/event/job_ran.rb +8 -0
- data/lib/startback/jobs/event.rb +8 -0
- data/lib/startback/jobs/model/job.rb +35 -0
- data/lib/startback/jobs/model.rb +12 -0
- data/lib/startback/jobs/operation/create_job.rb +37 -0
- data/lib/startback/jobs/operation/run_job.rb +35 -0
- data/lib/startback/jobs/operation.rb +8 -0
- data/lib/startback/jobs/services.rb +25 -0
- data/lib/startback/jobs/support/job_result/embedded.rb +15 -0
- data/lib/startback/jobs/support/job_result/not_ready.rb +15 -0
- data/lib/startback/jobs/support/job_result/redirect.rb +32 -0
- data/lib/startback/jobs/support/job_result.rb +31 -0
- data/lib/startback/jobs/support.rb +1 -0
- data/lib/startback/jobs.fio +44 -0
- data/lib/startback/jobs.rb +23 -0
- data/spec/spec_helper.rb +26 -33
- data/spec/unit/api/test_job_result.rb +121 -0
- data/spec/unit/model/test_job.rb +23 -0
- data/spec/unit/operation/test_create_job.rb +42 -0
- data/spec/unit/operation/test_run_job.rb +37 -0
- data/spec/unit/test_finitio_schema.rb +20 -0
- data/tasks/test.rake +0 -1
- metadata +29 -92
- data/README.md +0 -13
- data/lib/startback/audit/prometheus.rb +0 -87
- data/lib/startback/audit/shared.rb +0 -17
- data/lib/startback/audit/trailer.rb +0 -129
- data/lib/startback/audit.rb +0 -3
- data/lib/startback/caching/entity_cache.rb +0 -157
- data/lib/startback/caching/no_store.rb +0 -28
- data/lib/startback/caching/store.rb +0 -34
- data/lib/startback/context/h_factory.rb +0 -43
- data/lib/startback/context/middleware.rb +0 -53
- data/lib/startback/context.rb +0 -122
- data/lib/startback/errors.rb +0 -197
- data/lib/startback/event/agent.rb +0 -84
- data/lib/startback/event/bus/bunny/async.rb +0 -162
- data/lib/startback/event/bus/bunny.rb +0 -1
- data/lib/startback/event/bus/memory/async.rb +0 -45
- data/lib/startback/event/bus/memory/sync.rb +0 -35
- data/lib/startback/event/bus/memory.rb +0 -2
- data/lib/startback/event/bus.rb +0 -100
- data/lib/startback/event/engine.rb +0 -94
- data/lib/startback/event/ext/context.rb +0 -5
- data/lib/startback/event/ext/operation.rb +0 -13
- data/lib/startback/event.rb +0 -47
- data/lib/startback/ext/date_time.rb +0 -9
- data/lib/startback/ext/time.rb +0 -9
- data/lib/startback/model.rb +0 -6
- data/lib/startback/operation/error_operation.rb +0 -19
- data/lib/startback/operation/multi_operation.rb +0 -28
- data/lib/startback/operation.rb +0 -78
- data/lib/startback/services.rb +0 -11
- data/lib/startback/support/data_object.rb +0 -71
- data/lib/startback/support/env.rb +0 -41
- data/lib/startback/support/fake_logger.rb +0 -18
- data/lib/startback/support/hooks.rb +0 -48
- data/lib/startback/support/log_formatter.rb +0 -34
- data/lib/startback/support/logger.rb +0 -34
- data/lib/startback/support/operation_runner.rb +0 -150
- data/lib/startback/support/robustness.rb +0 -157
- data/lib/startback/support/transaction_manager.rb +0 -25
- data/lib/startback/support/transaction_policy.rb +0 -33
- data/lib/startback/support/world.rb +0 -54
- data/lib/startback/support.rb +0 -26
- data/lib/startback/version.rb +0 -8
- data/lib/startback/web/api.rb +0 -99
- data/lib/startback/web/auto_caching.rb +0 -85
- data/lib/startback/web/catch_all.rb +0 -52
- data/lib/startback/web/cors_headers.rb +0 -80
- data/lib/startback/web/health_check.rb +0 -49
- data/lib/startback/web/magic_assets/ng_html_transformer.rb +0 -80
- data/lib/startback/web/magic_assets/rake_tasks.rb +0 -64
- data/lib/startback/web/magic_assets.rb +0 -98
- data/lib/startback/web/middleware.rb +0 -13
- data/lib/startback/web/prometheus.rb +0 -16
- data/lib/startback/web/shield.rb +0 -58
- data/lib/startback.rb +0 -43
- data/spec/unit/audit/test_prometheus.rb +0 -72
- data/spec/unit/audit/test_trailer.rb +0 -105
- data/spec/unit/caching/test_entity_cache.rb +0 -136
- data/spec/unit/context/test_abstraction_factory.rb +0 -64
- data/spec/unit/context/test_dup.rb +0 -42
- data/spec/unit/context/test_fork.rb +0 -37
- data/spec/unit/context/test_h_factory.rb +0 -31
- data/spec/unit/context/test_middleware.rb +0 -45
- data/spec/unit/context/test_with_world.rb +0 -20
- data/spec/unit/context/test_world.rb +0 -17
- data/spec/unit/event/bus/memory/test_async.rb +0 -43
- data/spec/unit/event/bus/memory/test_sync.rb +0 -43
- data/spec/unit/support/hooks/test_after_hook.rb +0 -54
- data/spec/unit/support/hooks/test_before_hook.rb +0 -54
- data/spec/unit/support/operation_runner/test_around_run.rb +0 -156
- data/spec/unit/support/operation_runner/test_before_after_call.rb +0 -48
- data/spec/unit/support/test_data_object.rb +0 -156
- data/spec/unit/support/test_env.rb +0 -75
- data/spec/unit/support/test_robusteness.rb +0 -229
- data/spec/unit/support/test_transaction_manager.rb +0 -64
- data/spec/unit/support/test_world.rb +0 -72
- data/spec/unit/test_event.rb +0 -62
- data/spec/unit/test_operation.rb +0 -55
- data/spec/unit/test_support.rb +0 -40
- data/spec/unit/web/fixtures/assets/app/hello.es6 +0 -4
- data/spec/unit/web/fixtures/assets/app/hello.html +0 -1
- data/spec/unit/web/fixtures/assets/index.es6 +0 -1
- data/spec/unit/web/test_api.rb +0 -82
- data/spec/unit/web/test_auto_caching.rb +0 -81
- data/spec/unit/web/test_catch_all.rb +0 -77
- data/spec/unit/web/test_cors_headers.rb +0 -88
- data/spec/unit/web/test_healthcheck.rb +0 -59
- data/spec/unit/web/test_magic_assets.rb +0 -82
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Startback
|
4
|
+
module Jobs
|
5
|
+
describe CreateJob do
|
6
|
+
|
7
|
+
let(:request) do
|
8
|
+
{
|
9
|
+
isReady: false,
|
10
|
+
opClass: 'CowSay',
|
11
|
+
opInput: {},
|
12
|
+
opContext: {},
|
13
|
+
createdBy: 'blambeau',
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:jobs_relvar) do
|
18
|
+
Bmg.mutable([])
|
19
|
+
end
|
20
|
+
|
21
|
+
let(:context) do
|
22
|
+
Context.new.with_world(startback_jobs: jobs_relvar)
|
23
|
+
end
|
24
|
+
|
25
|
+
subject do
|
26
|
+
CreateJob.new(request).bind({
|
27
|
+
context: context,
|
28
|
+
}).call
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'creates a job' do
|
32
|
+
expect(subject).to be_a(Model::Job)
|
33
|
+
expect(subject.id).not_to be_nil
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'inserts the job in the relvar' do
|
37
|
+
subject
|
38
|
+
expect(jobs_relvar.count).to eql(1)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Startback
|
4
|
+
module Jobs
|
5
|
+
describe RunJob do
|
6
|
+
|
7
|
+
let(:job_data) do
|
8
|
+
a_job_data
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:jobs_relvar) do
|
12
|
+
Bmg.mutable([job_data])
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:context) do
|
16
|
+
Context.new.with_world(startback_jobs: jobs_relvar)
|
17
|
+
end
|
18
|
+
|
19
|
+
subject do
|
20
|
+
RunJob.new(id: 'abcdef').bind({
|
21
|
+
context: context,
|
22
|
+
}).call
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'runs the job' do
|
26
|
+
expect(subject).to eql('Hello !!')
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'updates the job' do
|
30
|
+
subject
|
31
|
+
job_info = jobs_relvar.one
|
32
|
+
expect(job_info[:opResult]).to eql('Hello !!')
|
33
|
+
expect(job_info[:isReady]).to eql(true)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Startback
|
4
|
+
module Jobs
|
5
|
+
describe 'Finitio schemas' do
|
6
|
+
|
7
|
+
it 'is correctly installed on stdlib' do
|
8
|
+
system = Finitio.system <<~FIO
|
9
|
+
@import startback/jobs
|
10
|
+
|
11
|
+
Job.Ref
|
12
|
+
FIO
|
13
|
+
expect {
|
14
|
+
system.dress({ id: "hello" })
|
15
|
+
}.not_to raise_error
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/tasks/test.rake
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: startback-jobs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.14.
|
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-
|
11
|
+
date: 2022-06-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -118,14 +118,14 @@ dependencies:
|
|
118
118
|
requirements:
|
119
119
|
- - '='
|
120
120
|
- !ruby/object:Gem::Version
|
121
|
-
version: 0.14.
|
121
|
+
version: 0.14.1
|
122
122
|
type: :runtime
|
123
123
|
prerelease: false
|
124
124
|
version_requirements: !ruby/object:Gem::Requirement
|
125
125
|
requirements:
|
126
126
|
- - '='
|
127
127
|
- !ruby/object:Gem::Version
|
128
|
-
version: 0.14.
|
128
|
+
version: 0.14.1
|
129
129
|
description: Asynchronous jobs on top of the Startback framework
|
130
130
|
email: blambeau@gmail.com
|
131
131
|
executables: []
|
@@ -133,97 +133,34 @@ extensions: []
|
|
133
133
|
extra_rdoc_files: []
|
134
134
|
files:
|
135
135
|
- Gemfile
|
136
|
-
- README.md
|
137
136
|
- Rakefile
|
138
|
-
- lib/startback.rb
|
139
|
-
- lib/startback/audit.rb
|
140
|
-
- lib/startback/audit/prometheus.rb
|
141
|
-
- lib/startback/audit/shared.rb
|
142
|
-
- lib/startback/audit/trailer.rb
|
143
|
-
- lib/startback/caching/entity_cache.rb
|
144
|
-
- lib/startback/caching/no_store.rb
|
145
|
-
- lib/startback/caching/store.rb
|
146
|
-
- lib/startback/context.rb
|
147
|
-
- lib/startback/context/h_factory.rb
|
148
|
-
- lib/startback/context/middleware.rb
|
149
|
-
- lib/startback/errors.rb
|
150
|
-
- lib/startback/event.rb
|
151
|
-
- lib/startback/event/agent.rb
|
152
|
-
- lib/startback/event/bus.rb
|
153
|
-
- lib/startback/event/bus/bunny.rb
|
154
|
-
- lib/startback/event/bus/bunny/async.rb
|
155
|
-
- lib/startback/event/bus/memory.rb
|
156
|
-
- lib/startback/event/bus/memory/async.rb
|
157
|
-
- lib/startback/event/bus/memory/sync.rb
|
158
|
-
- lib/startback/event/engine.rb
|
159
|
-
- lib/startback/event/ext/context.rb
|
160
|
-
- lib/startback/event/ext/operation.rb
|
161
137
|
- lib/startback/ext.rb
|
162
|
-
- lib/startback/ext/
|
163
|
-
- lib/startback/ext/
|
164
|
-
- lib/startback/
|
165
|
-
- lib/startback/
|
166
|
-
- lib/startback/
|
167
|
-
- lib/startback/
|
168
|
-
- lib/startback/
|
169
|
-
- lib/startback/
|
170
|
-
- lib/startback/
|
171
|
-
- lib/startback/
|
172
|
-
- lib/startback/
|
173
|
-
- lib/startback/
|
174
|
-
- lib/startback/
|
175
|
-
- lib/startback/
|
176
|
-
- lib/startback/
|
177
|
-
- lib/startback/support
|
178
|
-
- lib/startback/support/
|
179
|
-
- lib/startback/support/
|
180
|
-
- lib/startback/support/
|
181
|
-
- lib/startback/
|
182
|
-
- lib/startback/web/api.rb
|
183
|
-
- lib/startback/web/auto_caching.rb
|
184
|
-
- lib/startback/web/catch_all.rb
|
185
|
-
- lib/startback/web/cors_headers.rb
|
186
|
-
- lib/startback/web/health_check.rb
|
187
|
-
- lib/startback/web/magic_assets.rb
|
188
|
-
- lib/startback/web/magic_assets/ng_html_transformer.rb
|
189
|
-
- lib/startback/web/magic_assets/rake_tasks.rb
|
190
|
-
- lib/startback/web/middleware.rb
|
191
|
-
- lib/startback/web/prometheus.rb
|
192
|
-
- lib/startback/web/shield.rb
|
138
|
+
- lib/startback/ext/support/operation_runner.rb
|
139
|
+
- lib/startback/ext/web/api.rb
|
140
|
+
- lib/startback/jobs.fio
|
141
|
+
- lib/startback/jobs.rb
|
142
|
+
- lib/startback/jobs/agent.rb
|
143
|
+
- lib/startback/jobs/api.rb
|
144
|
+
- lib/startback/jobs/event.rb
|
145
|
+
- lib/startback/jobs/event/job_created.rb
|
146
|
+
- lib/startback/jobs/event/job_ran.rb
|
147
|
+
- lib/startback/jobs/model.rb
|
148
|
+
- lib/startback/jobs/model/job.rb
|
149
|
+
- lib/startback/jobs/operation.rb
|
150
|
+
- lib/startback/jobs/operation/create_job.rb
|
151
|
+
- lib/startback/jobs/operation/run_job.rb
|
152
|
+
- lib/startback/jobs/services.rb
|
153
|
+
- lib/startback/jobs/support.rb
|
154
|
+
- lib/startback/jobs/support/job_result.rb
|
155
|
+
- lib/startback/jobs/support/job_result/embedded.rb
|
156
|
+
- lib/startback/jobs/support/job_result/not_ready.rb
|
157
|
+
- lib/startback/jobs/support/job_result/redirect.rb
|
193
158
|
- spec/spec_helper.rb
|
194
|
-
- spec/unit/
|
195
|
-
- spec/unit/
|
196
|
-
- spec/unit/
|
197
|
-
- spec/unit/
|
198
|
-
- spec/unit/
|
199
|
-
- spec/unit/context/test_fork.rb
|
200
|
-
- spec/unit/context/test_h_factory.rb
|
201
|
-
- spec/unit/context/test_middleware.rb
|
202
|
-
- spec/unit/context/test_with_world.rb
|
203
|
-
- spec/unit/context/test_world.rb
|
204
|
-
- spec/unit/event/bus/memory/test_async.rb
|
205
|
-
- spec/unit/event/bus/memory/test_sync.rb
|
206
|
-
- spec/unit/support/hooks/test_after_hook.rb
|
207
|
-
- spec/unit/support/hooks/test_before_hook.rb
|
208
|
-
- spec/unit/support/operation_runner/test_around_run.rb
|
209
|
-
- spec/unit/support/operation_runner/test_before_after_call.rb
|
210
|
-
- spec/unit/support/test_data_object.rb
|
211
|
-
- spec/unit/support/test_env.rb
|
212
|
-
- spec/unit/support/test_robusteness.rb
|
213
|
-
- spec/unit/support/test_transaction_manager.rb
|
214
|
-
- spec/unit/support/test_world.rb
|
215
|
-
- spec/unit/test_event.rb
|
216
|
-
- spec/unit/test_operation.rb
|
217
|
-
- spec/unit/test_support.rb
|
218
|
-
- spec/unit/web/fixtures/assets/app/hello.es6
|
219
|
-
- spec/unit/web/fixtures/assets/app/hello.html
|
220
|
-
- spec/unit/web/fixtures/assets/index.es6
|
221
|
-
- spec/unit/web/test_api.rb
|
222
|
-
- spec/unit/web/test_auto_caching.rb
|
223
|
-
- spec/unit/web/test_catch_all.rb
|
224
|
-
- spec/unit/web/test_cors_headers.rb
|
225
|
-
- spec/unit/web/test_healthcheck.rb
|
226
|
-
- spec/unit/web/test_magic_assets.rb
|
159
|
+
- spec/unit/api/test_job_result.rb
|
160
|
+
- spec/unit/model/test_job.rb
|
161
|
+
- spec/unit/operation/test_create_job.rb
|
162
|
+
- spec/unit/operation/test_run_job.rb
|
163
|
+
- spec/unit/test_finitio_schema.rb
|
227
164
|
- tasks/test.rake
|
228
165
|
homepage: https://www.enspirit.be
|
229
166
|
licenses:
|
data/README.md
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
# Startback - Got Your Ruby Back
|
2
|
-
|
3
|
-
Yet another ruby framework, I'm afraid. Here, we srongly seperate between:
|
4
|
-
|
5
|
-
1. the web layer, in charge of a quality HTTP handling
|
6
|
-
2. the operations layer, in charge of the high-level software operations
|
7
|
-
3. the database layer, abstracted using the Relations As First Class Citizen pattern
|
8
|
-
|
9
|
-
Currently,
|
10
|
-
|
11
|
-
1. is handled using extra support on top of Sinatra
|
12
|
-
2. is handled using Startback specific classes
|
13
|
-
3. is handled using Bmg
|
@@ -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
|
data/lib/startback/audit.rb
DELETED
@@ -1,157 +0,0 @@
|
|
1
|
-
module Startback
|
2
|
-
module Caching
|
3
|
-
#
|
4
|
-
# A overriable caching abstraction aiming at making Entity-based caching easy.
|
5
|
-
#
|
6
|
-
# This class MUST be overriden:
|
7
|
-
#
|
8
|
-
# * the `load_entity` protected method MUST be implemented to load data from
|
9
|
-
# a primary & context unaware key.
|
10
|
-
#
|
11
|
-
# * the `primary_key` protected method MAY be implemented to convert candidate
|
12
|
-
# keys (received from ultimate callers) to primary keys. The method is also
|
13
|
-
# a good place to check and/or log the keys actually used by callers.
|
14
|
-
#
|
15
|
-
# * the `context_free_key` protected method MAY be overriden to provide
|
16
|
-
# domain unrelated caching keys from primary keys, e.g. by encoding the
|
17
|
-
# context into the caching key itself, if needed.
|
18
|
-
#
|
19
|
-
# * the `valid?` protected method MAY be overriden to check validity of data
|
20
|
-
# extracted from the cache and force a refresh even if found.
|
21
|
-
#
|
22
|
-
# An EntityCache takes an actual store at construction. The object must meet the
|
23
|
-
# specification writtern in Store. The 'cache' ruby gem can be used in practice.
|
24
|
-
#
|
25
|
-
# Cache hits, outdated and miss are logged in debug, info, and info severity.
|
26
|
-
# The `cache_hit`, `cache_outdated`, `cache_miss` protected methods MAY be
|
27
|
-
# overriden to change that behavior.
|
28
|
-
#
|
29
|
-
class EntityCache
|
30
|
-
include Support::Robustness
|
31
|
-
|
32
|
-
class << self
|
33
|
-
|
34
|
-
# Default time to live, in seconds
|
35
|
-
attr_writer :default_ttl
|
36
|
-
|
37
|
-
def default_ttl
|
38
|
-
@default_ttl || (superclass.respond_to?(:default_ttl, true) && superclass.default_ttl) || 3600
|
39
|
-
end
|
40
|
-
|
41
|
-
end # class DSL
|
42
|
-
|
43
|
-
def initialize(store, context = nil)
|
44
|
-
@store = store
|
45
|
-
@context = context
|
46
|
-
end
|
47
|
-
attr_reader :store, :context
|
48
|
-
|
49
|
-
# Returns the entity corresponding to a given key.
|
50
|
-
#
|
51
|
-
# If the entity is not in cache, loads it and puts it in cache using
|
52
|
-
# the caching options passed as second parameter.
|
53
|
-
def get(candidate_key, caching_options = default_caching_options)
|
54
|
-
pkey = primary_key(candidate_key)
|
55
|
-
cache_key = encode_key(context_free_key(pkey))
|
56
|
-
if store.exist?(cache_key)
|
57
|
-
cached = store.get(cache_key)
|
58
|
-
if valid?(pkey, cached)
|
59
|
-
cache_hit(pkey, cached)
|
60
|
-
return cached
|
61
|
-
else
|
62
|
-
cache_outdated(pkey, cached)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
cache_miss(pkey)
|
66
|
-
load_entity(pkey).tap{|to_cache|
|
67
|
-
store.set(cache_key, to_cache, caching_options)
|
68
|
-
}
|
69
|
-
end
|
70
|
-
|
71
|
-
# Invalidates the cache under a given key.
|
72
|
-
def invalidate(candidate_key)
|
73
|
-
pkey = primary_key(candidate_key)
|
74
|
-
cache_key = encode_key(context_free_key(pkey))
|
75
|
-
store.delete(cache_key)
|
76
|
-
end
|
77
|
-
|
78
|
-
protected
|
79
|
-
|
80
|
-
def cache_hit(pkey, cached)
|
81
|
-
log(:debug, self, "cache_hit", context, op_data: pkey)
|
82
|
-
end
|
83
|
-
|
84
|
-
def cache_outdated(pkey, cached)
|
85
|
-
log(:info, self, "cache_outdated", context, op_data: pkey)
|
86
|
-
end
|
87
|
-
|
88
|
-
def cache_miss(pkey)
|
89
|
-
log(:info, self, "cache_miss", context, op_data: pkey)
|
90
|
-
end
|
91
|
-
|
92
|
-
def default_caching_options
|
93
|
-
{ ttl: self.class.default_ttl }
|
94
|
-
end
|
95
|
-
|
96
|
-
# Converts a candidate key to a primary key, so as to prevent
|
97
|
-
# cache duplicates if callers are allowed to request an entity
|
98
|
-
# through various keys.
|
99
|
-
#
|
100
|
-
# The default implementation returns the candidate key and MAY
|
101
|
-
# be overriden.
|
102
|
-
def primary_key(candidate_key)
|
103
|
-
candidate_key
|
104
|
-
end
|
105
|
-
|
106
|
-
# Encodes a context free key to an actual cache key.
|
107
|
-
#
|
108
|
-
# Default implementation uses JSON.fast_generate but MAY be
|
109
|
-
# overriden.
|
110
|
-
def encode_key(context_free_key)
|
111
|
-
JSON.fast_generate(context_free_key)
|
112
|
-
end
|
113
|
-
|
114
|
-
# Returns whether `cached` entity seems fresh enough to
|
115
|
-
# be returned as a cache hit.
|
116
|
-
#
|
117
|
-
# This method provides a way to check freshness using, e.g.
|
118
|
-
# `updated_at` or `etag` kind of entity fields. The default
|
119
|
-
# implementation returns true and MAY be overriden.
|
120
|
-
def valid?(primary_key, cached)
|
121
|
-
true
|
122
|
-
end
|
123
|
-
|
124
|
-
# Converts a primary_key to a context_free_key, using the
|
125
|
-
# context (instance variable) to encode the context itself
|
126
|
-
# into the actual cache key.
|
127
|
-
#
|
128
|
-
# The default implementation simply returns the primary key
|
129
|
-
# and MAY be overriden.
|
130
|
-
def context_free_key(primary_key)
|
131
|
-
full_key(primary_key)
|
132
|
-
end
|
133
|
-
|
134
|
-
# Deprecated, will be removed in 0.6.0. Use context_free_key
|
135
|
-
# instead.
|
136
|
-
def full_key(primary_key)
|
137
|
-
primary_key
|
138
|
-
end
|
139
|
-
|
140
|
-
# Actually loads the entity using the given primary key, and
|
141
|
-
# possibly the cache context.
|
142
|
-
#
|
143
|
-
# This method MUST be implemented and raises a NotImplementedError
|
144
|
-
# by default.
|
145
|
-
def load_entity(primary_key)
|
146
|
-
load_raw_data(primary_key)
|
147
|
-
end
|
148
|
-
|
149
|
-
# Deprecated, will be removed in 0.6.0. Use load_entity
|
150
|
-
# instead.
|
151
|
-
def load_raw_data(*args, &bl)
|
152
|
-
raise NotImplementedError, "#{self.class.name}#load_entity"
|
153
|
-
end
|
154
|
-
|
155
|
-
end # class EntityCache
|
156
|
-
end # module Caching
|
157
|
-
end # module Startback
|