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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8ce9b4330b1d4ad0334d37f23d154149d01580c47f3f3c32d012f4b094244fac
4
- data.tar.gz: 11aa9a2571cf62454937a3e61b01b7de70bb702255b952d46f533d8831572ccd
3
+ metadata.gz: 7c95b320d216c89bd72c728b2b0402cae897d984554fd6c41e66739eac544e9b
4
+ data.tar.gz: 55bd64c8191703e6d5b6aac98945c54fd9d06a65b5b094e2c338ad64c9641fb8
5
5
  SHA512:
6
- metadata.gz: 07be4f5abcbfae46edd0c480a6b3766924979990c065b7833c41c50ff6ff6401ab8b0798a643d5da339cb3a71e885be62af6034d774a2d400befb6cdf9db3e61
7
- data.tar.gz: dfe33dbe27d11e5ac0604538c803158315e5fcdd61726ef0107734dc61aa53eff04f7e6e769c2dae6d1fa5d534682d0d8895eec90ef1cd5d8e8d926157474a56
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.specification.request_headers
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: recorded_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),
@@ -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
- # preferring to call `response.request` instead of preparing a new
22
- # request (via `ActionDispatch::Request.new(env)` and passing it
23
- # as an argument to this method because we can always recover the
24
- # specific request that elicited a given response, according to
25
- # https://github.com/rails/rails/blob/cacb8475a9d4373c0db437e7be4905685f03cefa/actionpack/lib/action_dispatch/http/response.rb#L53
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
- sig { params(request: Request, response: Response, metadata: Metadata, extras: Extras).returns(T.nilable(Conversation)) }
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(response: ActionDispatch::Response).returns(Archive::Conversation) }
9
- def record(response); end
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
- sig { override.params(_response: ActionDispatch::Response).returns(Archive::Conversation) }
10
- def record(_response) = Archive::Conversation.new
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
- sig { override.params(_response: ActionDispatch::Response).returns(Archive::Conversation) }
8
- def record(_response) = Archive::Conversation.new
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 :content_type_recorders, T::Hash[String, ConversationRecorder]
7
- const :request_content_types, T::Set[String]
8
- const :request_headers, T::Set[String]
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
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Vigiles
5
- VERSION = "0.1.0-beta7"
5
+ VERSION = "0.1.0-beta9"
6
6
  end
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.specification
21
- @specification ||= T.let(
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.specification=(spec)
29
- @specification = spec
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(specification)
43
+ blk.call(spec)
44
44
 
45
- # TODO(yaw, 2024-06-15): ensure that the specification is valid.
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
@@ -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.beta7
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-24 00:00:00.000000000 Z
11
+ date: 2024-05-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sorbet-runtime