vigiles 0.1.0.pre.beta7 → 0.1.0.pre.beta9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/vigiles/archive/request.rb +11 -2
- data/lib/vigiles/archive.rb +6 -58
- data/lib/vigiles/conversation_recorder.rb +17 -2
- data/lib/vigiles/conversation_recorders/application_json.rb +39 -2
- data/lib/vigiles/conversation_recorders/unknown.rb +4 -2
- data/lib/vigiles/spec.rb +5 -5
- data/lib/vigiles/version.rb +1 -1
- data/lib/vigiles.rb +6 -6
- data/sorbet/tapioca/require.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7c95b320d216c89bd72c728b2b0402cae897d984554fd6c41e66739eac544e9b
|
4
|
+
data.tar.gz: 55bd64c8191703e6d5b6aac98945c54fd9d06a65b5b094e2c338ad64c9641fb8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e98f9cf83934a297eed7ac621f0200b118610690ccd1c9f51279759edce9bfa0db23bd4161adcd762a891b7a3aad6c3606d3e2d66b5ae566615c971ee3bd6460
|
7
|
+
data.tar.gz: 8b90d7d720d5104d3280aefbf6065892f24d677489ae6c096bab31a887fbc4237ac76765f0425a16ed011aaee2dba4c2828f90a6411d6cc6dff95e761eddf943
|
@@ -35,12 +35,21 @@ module Vigiles
|
|
35
35
|
const :url, T.any(URI::HTTPS, URI::HTTP)
|
36
36
|
const :id, String
|
37
37
|
|
38
|
+
sig { params(header_key: String).returns(String) }
|
39
|
+
private_class_method def self.best_effort_unfuck_http_header(header_key)
|
40
|
+
(header_key.starts_with?("HTTP_") ? T.must(header_key[5..]) : header_key)
|
41
|
+
.split(/_/)
|
42
|
+
.map(&:titlecase)
|
43
|
+
.join("-")
|
44
|
+
end
|
45
|
+
|
38
46
|
sig { params(request: ActionDispatch::Request).returns(Request) }
|
39
47
|
def self.from(request)
|
40
|
-
preferred_headers = Vigiles.
|
48
|
+
preferred_headers = Vigiles.spec.request_headers
|
41
49
|
available_headers = request.original_headers
|
42
50
|
recorded_headers = (available_headers if preferred_headers.empty?)
|
43
51
|
recorded_headers ||= preferred_headers.to_h { |h| [h, available_headers[h]] }
|
52
|
+
unfucked_headers = recorded_headers.transform_keys { best_effort_unfuck_http_header _1 }
|
44
53
|
|
45
54
|
Request.new(
|
46
55
|
content_type: request.content_type || (raise InvalidParameterError, "content_type"),
|
@@ -48,7 +57,7 @@ module Vigiles
|
|
48
57
|
timestamp: DateTime.now,
|
49
58
|
remote_ip: IPAddr.new(request.remote_ip),
|
50
59
|
protocol: request.protocol,
|
51
|
-
headers:
|
60
|
+
headers: unfucked_headers,
|
52
61
|
origin: request.origin || "unknown_origin_url",
|
53
62
|
payload: request.body.read,
|
54
63
|
http_method: Types::HttpMethod.deserialize(request.method),
|
data/lib/vigiles/archive.rb
CHANGED
@@ -18,66 +18,14 @@ module Vigiles
|
|
18
18
|
|
19
19
|
sig { params(req: ActionDispatch::Request, res: Rack::Response).returns(T.nilable(Conversation)) }
|
20
20
|
def self.record_conversation(req:, res:)
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
response = Response.from(res)
|
27
|
-
metadata = Metadata.from(req.env)
|
28
|
-
request = Request.from(req)
|
29
|
-
extras = Extras.from(req.env)
|
30
|
-
|
31
|
-
case (content_type = request.content_type)
|
32
|
-
when ContentType::ApplicationJson.serialize then record_json_conversation(request:, response:, metadata:, extras:)
|
33
|
-
when ContentType::TextHtml.serialize then record_html_conversation(request:, response:, metadata:, extras:)
|
34
|
-
else record_conversation_with_unknown_content_type(request:, response:, metadata:, extras:)
|
21
|
+
content_type = req.content_type
|
22
|
+
if (recorder = Vigiles.spec.recorders[content_type]).nil?
|
23
|
+
raise \
|
24
|
+
UnrecordableRequestError,
|
25
|
+
"no recorder configured for content type: #{content_type}"
|
35
26
|
end
|
36
|
-
rescue Request::InvalidParameterError => e
|
37
|
-
raise UnrecordableRequestError, "#{e.parameter} considered invalid"
|
38
|
-
end
|
39
|
-
|
40
|
-
sig do
|
41
|
-
params(
|
42
|
-
request: Request,
|
43
|
-
response: Response,
|
44
|
-
metadata: Metadata,
|
45
|
-
extras: Extras
|
46
|
-
)
|
47
|
-
.returns(T.nilable(Conversation))
|
48
|
-
end
|
49
|
-
private_class_method def self.record_json_conversation(request:, response:, metadata:, extras:)
|
50
|
-
Conversation.create!(
|
51
|
-
request_content_type: request.content_type,
|
52
|
-
request_user_agent: request.user_agent,
|
53
|
-
request_timestamp: request.timestamp,
|
54
|
-
request_remote_ip: request.remote_ip,
|
55
|
-
request_protocol: request.protocol,
|
56
|
-
request_headers: request.headers,
|
57
|
-
request_origin: request.origin,
|
58
|
-
request_payload: request.payload,
|
59
|
-
request_method: request.http_method.serialize,
|
60
|
-
request_path: request.path,
|
61
|
-
request_url: request.url,
|
62
|
-
request_id: request.id,
|
63
|
-
response_content_type: response.content_type,
|
64
|
-
response_headers: response.headers,
|
65
|
-
response_payload: response.payload,
|
66
|
-
response_status: response.status
|
67
|
-
)
|
68
|
-
rescue => e
|
69
|
-
end
|
70
|
-
|
71
|
-
sig { params(request: Request, response: Response, metadata: Metadata, extras: Extras).returns(T.nilable(Conversation)) }
|
72
|
-
private_class_method def self.record_html_conversation(request:, response:, metadata:, extras:); end
|
73
27
|
|
74
|
-
|
75
|
-
private_class_method def self.record_conversation_with_unknown_content_type(
|
76
|
-
request:,
|
77
|
-
response:,
|
78
|
-
metadata:,
|
79
|
-
extras:
|
80
|
-
)
|
28
|
+
recorder.record(req:, res:)
|
81
29
|
end
|
82
30
|
end
|
83
31
|
end
|
@@ -3,9 +3,24 @@
|
|
3
3
|
|
4
4
|
module Vigiles
|
5
5
|
class ConversationRecorder
|
6
|
+
class MisconfiguredRecorderError < StandardError
|
7
|
+
sig { returns(String) }
|
8
|
+
attr_reader :expected
|
9
|
+
|
10
|
+
sig { returns(String) }
|
11
|
+
attr_reader :actual
|
12
|
+
|
13
|
+
sig { params(expected: String, actual: String).void }
|
14
|
+
def initialize(expected:, actual:)
|
15
|
+
@expected = expected
|
16
|
+
@actual = actual
|
17
|
+
super
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
6
21
|
abstract!
|
7
22
|
|
8
|
-
sig { abstract.params(
|
9
|
-
def record(
|
23
|
+
sig { abstract.params(req: ActionDispatch::Request, res: Rack::Response).returns(Archive::Conversation) }
|
24
|
+
def record(req:, res:); end
|
10
25
|
end
|
11
26
|
end
|
@@ -6,8 +6,45 @@ module Vigiles
|
|
6
6
|
class ApplicationJson < ConversationRecorder
|
7
7
|
include Singleton
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
ConversationRecorder = Vigiles::ConversationRecorder
|
10
|
+
ContentType = Vigiles::Types::ContentType
|
11
|
+
Conversation = Vigiles::Archive::Conversation
|
12
|
+
Response = Vigiles::Archive::Response
|
13
|
+
Metadata = Vigiles::Archive::Metadata
|
14
|
+
Request = Vigiles::Archive::Request
|
15
|
+
Extras = Vigiles::Archive::Extras
|
16
|
+
|
17
|
+
sig { override.params(req: ActionDispatch::Request, res: Rack::Response).returns(Archive::Conversation) }
|
18
|
+
def record(req:, res:)
|
19
|
+
unless req.content_type == ContentType::ApplicationJson.serialize
|
20
|
+
raise ConversationRecorder::MisconfiguredRecorderError.new(
|
21
|
+
expected: ContentType::ApplicationJson.serialize,
|
22
|
+
actual: req.content_type
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
response = Response.from(res)
|
27
|
+
request = Request.from(req)
|
28
|
+
|
29
|
+
Conversation.create!(
|
30
|
+
request_content_type: request.content_type,
|
31
|
+
request_user_agent: request.user_agent,
|
32
|
+
request_timestamp: request.timestamp,
|
33
|
+
request_remote_ip: request.remote_ip,
|
34
|
+
request_protocol: request.protocol,
|
35
|
+
request_headers: request.headers,
|
36
|
+
request_origin: request.origin,
|
37
|
+
request_payload: request.payload,
|
38
|
+
request_method: request.http_method.serialize,
|
39
|
+
request_path: request.path,
|
40
|
+
request_url: request.url,
|
41
|
+
request_id: request.id,
|
42
|
+
response_content_type: response.content_type,
|
43
|
+
response_headers: response.headers,
|
44
|
+
response_payload: response.payload,
|
45
|
+
response_status: response.status
|
46
|
+
)
|
47
|
+
end
|
11
48
|
end
|
12
49
|
end
|
13
50
|
end
|
@@ -4,8 +4,10 @@
|
|
4
4
|
module Vigiles
|
5
5
|
module ConversationRecorders
|
6
6
|
class Unknown < ConversationRecorder
|
7
|
-
|
8
|
-
|
7
|
+
include Singleton
|
8
|
+
|
9
|
+
sig { override.params(req: ActionDispatch::Request, res: Rack::Response).returns(Archive::Conversation) }
|
10
|
+
def record(req:, res:) = Archive::Conversation.new
|
9
11
|
end
|
10
12
|
end
|
11
13
|
end
|
data/lib/vigiles/spec.rb
CHANGED
@@ -3,16 +3,16 @@
|
|
3
3
|
|
4
4
|
module Vigiles
|
5
5
|
class Spec < T::Struct
|
6
|
-
const :
|
7
|
-
const :
|
8
|
-
const :
|
6
|
+
const :request_content_types, T::Set[String]
|
7
|
+
const :request_headers, T::Set[String]
|
8
|
+
const :recorders, T::Hash[String, ConversationRecorder]
|
9
9
|
|
10
10
|
sig { returns(Spec) }
|
11
11
|
def self.make_default_spec
|
12
12
|
Spec.new(
|
13
|
-
content_type_recorders: Constants::DEFAULT_CONTENT_TYPE_RECORDERS,
|
14
13
|
request_content_types: Constants::DEFAULT_CONTENT_TYPES,
|
15
|
-
request_headers: Set[]
|
14
|
+
request_headers: Set[].freeze,
|
15
|
+
recorders: Constants::DEFAULT_CONTENT_TYPE_RECORDERS
|
16
16
|
)
|
17
17
|
end
|
18
18
|
end
|
data/lib/vigiles/version.rb
CHANGED
data/lib/vigiles.rb
CHANGED
@@ -17,16 +17,16 @@ module Vigiles
|
|
17
17
|
extend T::Sig
|
18
18
|
|
19
19
|
sig { returns(Vigiles::Spec) }
|
20
|
-
def self.
|
21
|
-
@
|
20
|
+
def self.spec
|
21
|
+
@spec ||= T.let(
|
22
22
|
Vigiles::Spec.make_default_spec,
|
23
23
|
T.nilable(Vigiles::Spec)
|
24
24
|
)
|
25
25
|
end
|
26
26
|
|
27
27
|
sig { params(spec: Vigiles::Spec).returns(Vigiles::Spec) }
|
28
|
-
def self.
|
29
|
-
@
|
28
|
+
def self.spec=(spec)
|
29
|
+
@spec = spec
|
30
30
|
end
|
31
31
|
|
32
32
|
sig { params(req: ActionDispatch::Request, res: Rack::Response).returns(T.nilable(Archive::Conversation)) }
|
@@ -40,9 +40,9 @@ module Vigiles
|
|
40
40
|
|
41
41
|
sig { params(blk: T.untyped).void }
|
42
42
|
def self.configure(&blk)
|
43
|
-
blk.call(
|
43
|
+
blk.call(spec)
|
44
44
|
|
45
|
-
# TODO(yaw, 2024-06-15): ensure that the
|
45
|
+
# TODO(yaw, 2024-06-15): ensure that the spec is valid.
|
46
46
|
# ensure that for every content type a recorder is configured. otherwise
|
47
47
|
# assign the general recorder for unknown content types.
|
48
48
|
end
|
data/sorbet/tapioca/require.rb
CHANGED
@@ -3,10 +3,10 @@
|
|
3
3
|
|
4
4
|
require "action_dispatch"
|
5
5
|
require "active_record"
|
6
|
+
require "logger"
|
6
7
|
require "minitest/autorun"
|
7
8
|
require "rails/generators"
|
8
9
|
require "rails/generators/active_record"
|
9
10
|
require "securerandom"
|
10
11
|
require "sorbet-runtime"
|
11
|
-
require "uri"
|
12
12
|
require "zeitwerk"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vigiles
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.0.pre.
|
4
|
+
version: 0.1.0.pre.beta9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yaw Boakye
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-05-
|
11
|
+
date: 2024-05-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sorbet-runtime
|