end_point_blank 0.2.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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +13 -0
- data/.ruby-version +1 -0
- data/README.md +64 -0
- data/Rakefile +12 -0
- data/build.sh +5 -0
- data/docs/superpowers/plans/2026-07-08-framework-agnostic-core.md +68 -0
- data/docs/superpowers/specs/2026-07-08-framework-agnostic-core-design.md +58 -0
- data/end_point_blank.gemspec +45 -0
- data/lib/end_point_blank/access_tokens.rb +72 -0
- data/lib/end_point_blank/authorization.rb +36 -0
- data/lib/end_point_blank/commands/authentication_cache.rb +95 -0
- data/lib/end_point_blank/commands/basic_authenticate.rb +47 -0
- data/lib/end_point_blank/commands/bearer_generate.rb +33 -0
- data/lib/end_point_blank/commands/endpoint_authorize.rb +71 -0
- data/lib/end_point_blank/commands/endpoint_update.rb +129 -0
- data/lib/end_point_blank/commands/generate_access_token.rb +46 -0
- data/lib/end_point_blank/commands/http.rb +45 -0
- data/lib/end_point_blank/commands/route_pattern_finder.rb +21 -0
- data/lib/end_point_blank/commands/version_finder.rb +70 -0
- data/lib/end_point_blank/configuration.rb +107 -0
- data/lib/end_point_blank/fast_json_truncator.rb +49 -0
- data/lib/end_point_blank/log_entry.rb +16 -0
- data/lib/end_point_blank/loggers/logger.rb +30 -0
- data/lib/end_point_blank/masking.rb +282 -0
- data/lib/end_point_blank/middleware/rack/report_interaction.rb +54 -0
- data/lib/end_point_blank/rack/env_store.rb +34 -0
- data/lib/end_point_blank/rack/headers.rb +11 -0
- data/lib/end_point_blank/rails/authenticated.rb +21 -0
- data/lib/end_point_blank/rails/authorized.rb +33 -0
- data/lib/end_point_blank/rails/railtie.rb +18 -0
- data/lib/end_point_blank/rails/versioned.rb +64 -0
- data/lib/end_point_blank/session_configuration.rb +44 -0
- data/lib/end_point_blank/string_truncator.rb +21 -0
- data/lib/end_point_blank/unauthorized_error.rb +10 -0
- data/lib/end_point_blank/version.rb +5 -0
- data/lib/end_point_blank/writers/delayed_writer.rb +110 -0
- data/lib/end_point_blank/writers/direct_writer.rb +18 -0
- data/lib/end_point_blank/writers/exception_writer.rb +50 -0
- data/lib/end_point_blank/writers/log_writer.rb +69 -0
- data/lib/end_point_blank/writers/request_writer.rb +61 -0
- data/lib/end_point_blank/writers/response_writer.rb +70 -0
- data/lib/end_point_blank/writers/shared.rb +39 -0
- data/lib/end_point_blank/xml_truncator.rb +89 -0
- data/lib/end_point_blank.rb +57 -0
- data/sig/end_point_blank_rack.rbs +4 -0
- data/test.sh +5 -0
- metadata +163 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
require 'singleton'
|
|
2
|
+
|
|
3
|
+
module EndPointBlank
|
|
4
|
+
module Writers
|
|
5
|
+
# Drains a background send queue with a fixed pool of worker threads.
|
|
6
|
+
#
|
|
7
|
+
# The queue is bounded (see MAX_QUEUE_SIZE) so that during an intake
|
|
8
|
+
# outage - when the worker threads can't drain as fast as the host
|
|
9
|
+
# application enqueues payloads - memory usage stays capped instead of
|
|
10
|
+
# growing without bound toward an OOM. When the queue is full, the
|
|
11
|
+
# *oldest* item is dropped to make room for the newest one, and a
|
|
12
|
+
# warning is logged, throttled so a sustained outage does not itself
|
|
13
|
+
# become a logging flood.
|
|
14
|
+
module DelayedWriter
|
|
15
|
+
MAX_QUEUE_SIZE = 1000
|
|
16
|
+
WARN_THROTTLE_SECONDS = 30
|
|
17
|
+
|
|
18
|
+
def direct_writer
|
|
19
|
+
@direct_writer ||= DirectWriter.new(url)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def queue
|
|
23
|
+
@queue ||= Queue.new
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def start_threads
|
|
27
|
+
@threads = []
|
|
28
|
+
|
|
29
|
+
2.times do
|
|
30
|
+
@threads << Thread.new do
|
|
31
|
+
loop do
|
|
32
|
+
payload = queue.pop
|
|
33
|
+
payloads = [payload]
|
|
34
|
+
while (payload = pop_additional) do
|
|
35
|
+
payloads << payload
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
payloads.compact!
|
|
39
|
+
while payloads.any? do
|
|
40
|
+
list = payloads[0..5]
|
|
41
|
+
response = direct_writer.write(list)
|
|
42
|
+
if response.status < 299
|
|
43
|
+
on_success(response) if respond_to?(:on_success)
|
|
44
|
+
else
|
|
45
|
+
on_failure(response) if respond_to?(:on_failure)
|
|
46
|
+
end
|
|
47
|
+
payloads = payloads - list
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def pop_additional
|
|
55
|
+
queue.pop(true)
|
|
56
|
+
rescue ThreadError
|
|
57
|
+
nil
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def enqueue(list)
|
|
61
|
+
items = list.is_a?(Array) ? list : [list]
|
|
62
|
+
items.each { |payload| enqueue_one(payload) }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Logs a drop warning. Overridable/stubbable in tests; routed through
|
|
66
|
+
# the pluggable EndPointBlank.logger seam (this lib is fire-and-forget
|
|
67
|
+
# and must never raise).
|
|
68
|
+
def log_warning(message)
|
|
69
|
+
EndPointBlank.logger.warn(message)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
def enqueue_mutex
|
|
75
|
+
@enqueue_mutex ||= Mutex.new
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def enqueue_one(payload)
|
|
79
|
+
enqueue_mutex.synchronize do
|
|
80
|
+
drop_oldest_and_note if queue.size >= MAX_QUEUE_SIZE
|
|
81
|
+
queue << payload
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def drop_oldest_and_note
|
|
86
|
+
pop_additional
|
|
87
|
+
note_drop
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def note_drop
|
|
91
|
+
@dropped_since_last_warning = (@dropped_since_last_warning || 0) + 1
|
|
92
|
+
now = Time.now
|
|
93
|
+
return if @last_drop_warning_at && (now - @last_drop_warning_at) < WARN_THROTTLE_SECONDS
|
|
94
|
+
|
|
95
|
+
warn_dropped_items(now)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def warn_dropped_items(now)
|
|
99
|
+
dropped = @dropped_since_last_warning
|
|
100
|
+
@dropped_since_last_warning = 0
|
|
101
|
+
@last_drop_warning_at = now
|
|
102
|
+
|
|
103
|
+
log_warning(
|
|
104
|
+
"[EndPointBlank] send queue full (max #{MAX_QUEUE_SIZE}); " \
|
|
105
|
+
"dropped #{dropped} oldest item(s) since last warning"
|
|
106
|
+
)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require_relative '../commands/http'
|
|
2
|
+
|
|
3
|
+
module EndPointBlank
|
|
4
|
+
module Writers
|
|
5
|
+
class DirectWriter
|
|
6
|
+
attr_accessor :url
|
|
7
|
+
|
|
8
|
+
def initialize(url)
|
|
9
|
+
@url = url
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def write(list)
|
|
13
|
+
auth = EndPointBlank::Authorization.header
|
|
14
|
+
EndPointBlank::Commands::Http.post(@url, auth, { payload: list })
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "singleton"
|
|
4
|
+
|
|
5
|
+
module EndPointBlank
|
|
6
|
+
module Writers
|
|
7
|
+
class ExceptionWriter
|
|
8
|
+
include Singleton
|
|
9
|
+
include DelayedWriter
|
|
10
|
+
include Shared
|
|
11
|
+
|
|
12
|
+
attr_reader :url
|
|
13
|
+
|
|
14
|
+
def initialize
|
|
15
|
+
@url = EndPointBlank::Configuration.instance.errors_url
|
|
16
|
+
start_threads
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.write(exception)
|
|
20
|
+
instance.write(exception)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def payload(exception)
|
|
24
|
+
env = ::EndPointBlank::Rack::EnvStore.get
|
|
25
|
+
request = ::Rack::Request.new(env)
|
|
26
|
+
version = Commands::VersionFinder.new.find(request)
|
|
27
|
+
{
|
|
28
|
+
app_name: app_name,
|
|
29
|
+
uuid: request_uuid(env),
|
|
30
|
+
message: exception.message,
|
|
31
|
+
stacktrace: exception.backtrace,
|
|
32
|
+
sent_at: Time.now.utc.iso8601(3),
|
|
33
|
+
source_application_environment_id: source_application_environment_id
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def write(exception)
|
|
38
|
+
p = payload(exception)
|
|
39
|
+
rack_req = ::EndPointBlank::Rack::EnvStore.request
|
|
40
|
+
if rack_req
|
|
41
|
+
p = p.merge(
|
|
42
|
+
stamped_path: rack_req.path,
|
|
43
|
+
stamped_http_method: rack_req.request_method
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
enqueue(apply_masking(p, :error))
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "singleton"
|
|
4
|
+
|
|
5
|
+
module EndPointBlank
|
|
6
|
+
module Writers
|
|
7
|
+
class LogWriter
|
|
8
|
+
include Singleton
|
|
9
|
+
include DelayedWriter
|
|
10
|
+
include Shared
|
|
11
|
+
|
|
12
|
+
attr_reader :url
|
|
13
|
+
|
|
14
|
+
def initialize
|
|
15
|
+
@url = EndPointBlank::Configuration.instance.logs_url
|
|
16
|
+
start_threads
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.info(message, data = {})
|
|
20
|
+
instance.write(message, :info, data)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.warn(message, data = {})
|
|
24
|
+
instance.write(message, :warn, data)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.error(message, data = {})
|
|
28
|
+
instance.write(message, :error, data)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.fatal(message, data = {})
|
|
32
|
+
instance.write(message, :fatal, data)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.write(message, level, data = {})
|
|
36
|
+
instance.write(message, level, data)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def payload(message: message, level: level, data: data)
|
|
40
|
+
env = ::EndPointBlank::Rack::EnvStore.get
|
|
41
|
+
uuid = request_uuid(env)
|
|
42
|
+
|
|
43
|
+
{
|
|
44
|
+
message: message,
|
|
45
|
+
log_level: level,
|
|
46
|
+
sent_at: Time.now.utc.iso8601(3),
|
|
47
|
+
app_name: app_name,
|
|
48
|
+
uuid: uuid,
|
|
49
|
+
data: data,
|
|
50
|
+
source_application_environment_id: source_application_environment_id
|
|
51
|
+
}
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def write(message, level, data = {})
|
|
56
|
+
json = payload(message: message, level: level, data: data)
|
|
57
|
+
rack_req = ::EndPointBlank::Rack::EnvStore.request
|
|
58
|
+
if rack_req
|
|
59
|
+
json = json.merge(
|
|
60
|
+
stamped_path: rack_req.path,
|
|
61
|
+
stamped_http_method: rack_req.request_method
|
|
62
|
+
)
|
|
63
|
+
end
|
|
64
|
+
puts "Writing log: #{json}"
|
|
65
|
+
enqueue(json)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "singleton"
|
|
4
|
+
|
|
5
|
+
module EndPointBlank
|
|
6
|
+
module Writers
|
|
7
|
+
class RequestWriter
|
|
8
|
+
include Singleton
|
|
9
|
+
include DelayedWriter
|
|
10
|
+
include Shared
|
|
11
|
+
|
|
12
|
+
attr_reader :url
|
|
13
|
+
|
|
14
|
+
def initialize
|
|
15
|
+
@url = EndPointBlank::Configuration.instance.requests_url
|
|
16
|
+
start_threads
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.write(data = {})
|
|
20
|
+
instance.write()
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def payload
|
|
24
|
+
env = ::EndPointBlank::Rack::EnvStore.get
|
|
25
|
+
request = ::Rack::Request.new(env)
|
|
26
|
+
version = Commands::VersionFinder.new.find(request)
|
|
27
|
+
headers = ::EndPointBlank::Rack::Headers.extract
|
|
28
|
+
|
|
29
|
+
payload = {
|
|
30
|
+
app_name: EndPointBlank::Configuration.instance.app_name,
|
|
31
|
+
env: SessionConfiguration.env_name,
|
|
32
|
+
uuid: request_uuid(env),
|
|
33
|
+
host: request.host,
|
|
34
|
+
status: request_status(request),
|
|
35
|
+
headers: headers,
|
|
36
|
+
path: request.path,
|
|
37
|
+
http_method: request.request_method,
|
|
38
|
+
endpoint_version: version,
|
|
39
|
+
request: request_body(request),
|
|
40
|
+
sent_at: Time.now.utc.iso8601(3)
|
|
41
|
+
}
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def write()
|
|
45
|
+
enqueue(apply_masking(payload, :request))
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def request_status(request)
|
|
51
|
+
request.respond_to?(:status) ? request.status : nil
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def request_body(request)
|
|
55
|
+
body = request.body&.read
|
|
56
|
+
request.body.rewind if request.body&.respond_to?(:rewind)
|
|
57
|
+
body
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "singleton"
|
|
4
|
+
|
|
5
|
+
module EndPointBlank
|
|
6
|
+
module Writers
|
|
7
|
+
class ResponseWriter
|
|
8
|
+
include Singleton
|
|
9
|
+
include DelayedWriter
|
|
10
|
+
include Shared
|
|
11
|
+
|
|
12
|
+
attr_reader :url
|
|
13
|
+
|
|
14
|
+
def initialize
|
|
15
|
+
@url = EndPointBlank::Configuration.instance.responses_url
|
|
16
|
+
start_threads
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.write(status:, headers: {}, body: nil, data: {})
|
|
20
|
+
instance.write(status:, headers:, body:, data:)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def payload(status:, headers:, body:, data: {})
|
|
24
|
+
request = ::EndPointBlank::Rack::EnvStore.request
|
|
25
|
+
env = ::EndPointBlank::Rack::EnvStore.get
|
|
26
|
+
headers = ::EndPointBlank::Rack::Headers.extract
|
|
27
|
+
version = request ? Commands::VersionFinder.new.find(request) : nil
|
|
28
|
+
route = request ? Commands::RoutePatternFinder.find(request) : nil
|
|
29
|
+
|
|
30
|
+
{
|
|
31
|
+
app_name: app_name,
|
|
32
|
+
env: env_name,
|
|
33
|
+
uuid: request_uuid(env),
|
|
34
|
+
status: status,
|
|
35
|
+
headers: headers,
|
|
36
|
+
body: truncate(normalize_body(body)),
|
|
37
|
+
sent_at: Time.now.utc.iso8601(3),
|
|
38
|
+
route: route,
|
|
39
|
+
data: data,
|
|
40
|
+
source_application_environment_id: source_application_environment_id
|
|
41
|
+
}
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def write(status:, headers: {}, body: nil, data: {})
|
|
45
|
+
enqueue(apply_masking(payload(status:, headers:, body:, data:), :response))
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
def truncate(body)
|
|
50
|
+
return body if body.nil? || body.length <= 1024
|
|
51
|
+
|
|
52
|
+
body[0...1024] + "..."
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def normalize_body(body)
|
|
56
|
+
return nil if body.nil?
|
|
57
|
+
|
|
58
|
+
if body.respond_to?(:body) && !body.is_a?(Array)
|
|
59
|
+
body.body
|
|
60
|
+
elsif body.respond_to?(:each)
|
|
61
|
+
result = +""
|
|
62
|
+
body.each { |chunk| result << chunk }
|
|
63
|
+
result
|
|
64
|
+
else
|
|
65
|
+
body.to_s
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module EndPointBlank
|
|
2
|
+
module Writers
|
|
3
|
+
module Shared
|
|
4
|
+
attr_accessor :url
|
|
5
|
+
|
|
6
|
+
def configuration
|
|
7
|
+
Configuration.instance
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def app_name
|
|
11
|
+
configuration.app_name
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def env_name
|
|
15
|
+
SessionConfiguration.env_name
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def env
|
|
19
|
+
::EndPointBlank::Rack::EnvStore.get
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def source_application_environment_id
|
|
23
|
+
::EndPointBlank::Rack::EnvStore.source_application_environment_id
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Rails' ActionDispatch::Request#uuid simply reads this same Rack env
|
|
27
|
+
# key, so this is behavior-preserving when running under Rails, and
|
|
28
|
+
# gracefully returns nil when it isn't (plain Rack / Sinatra), instead
|
|
29
|
+
# of requiring actionpack's ActionDispatch::Request to be loaded.
|
|
30
|
+
def request_uuid(env)
|
|
31
|
+
env && env["action_dispatch.request_id"]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def apply_masking(payload, record_type)
|
|
35
|
+
EndPointBlank::Masking.apply(payload, record_type, configuration.masking_rules, configuration.mask_hook)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
require "rexml/document"
|
|
2
|
+
require "rexml/formatters/default"
|
|
3
|
+
|
|
4
|
+
class XmlTruncator
|
|
5
|
+
MAX_BYTES = 10000
|
|
6
|
+
MAX_DEPTH = 6
|
|
7
|
+
MAX_CHILDREN = 20
|
|
8
|
+
MAX_ATTRIBUTES = 20
|
|
9
|
+
MAX_TEXT = 200
|
|
10
|
+
TEXT_SUFFIX = "..."
|
|
11
|
+
|
|
12
|
+
def self.truncate(xml, limit: MAX_BYTES)
|
|
13
|
+
input = xml.to_s
|
|
14
|
+
return "" if input.empty?
|
|
15
|
+
return input if input.bytesize <= limit
|
|
16
|
+
|
|
17
|
+
document = parse_xml(input)
|
|
18
|
+
return StringTruncator.truncate(input, limit:, suffix: "<truncated/>") unless document&.root
|
|
19
|
+
|
|
20
|
+
pruned_root = prune_element(document.root, depth: 0)
|
|
21
|
+
output = render_element(pruned_root)
|
|
22
|
+
return output if output.bytesize <= limit
|
|
23
|
+
|
|
24
|
+
compact = compact_fallback(document.root.expanded_name)
|
|
25
|
+
return compact if compact.bytesize <= limit
|
|
26
|
+
|
|
27
|
+
"<truncated/>"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.parse_xml(input)
|
|
31
|
+
REXML::Document.new(input)
|
|
32
|
+
rescue REXML::ParseException
|
|
33
|
+
nil
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.prune_element(element, depth:)
|
|
37
|
+
pruned = REXML::Element.new(element.expanded_name)
|
|
38
|
+
|
|
39
|
+
element.attributes.each_with_index do |(name, value), idx|
|
|
40
|
+
break if idx >= MAX_ATTRIBUTES
|
|
41
|
+
pruned.add_attribute(name, truncate_text(value, max: 100))
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
if depth >= MAX_DEPTH
|
|
45
|
+
pruned.add_element("truncated")
|
|
46
|
+
return pruned
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
element_count = 0
|
|
50
|
+
|
|
51
|
+
element.children.each do |child|
|
|
52
|
+
case child
|
|
53
|
+
when REXML::Element
|
|
54
|
+
if element_count >= MAX_CHILDREN
|
|
55
|
+
pruned.add_element("truncated")
|
|
56
|
+
break
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
pruned.add_element(prune_element(child, depth: depth + 1))
|
|
60
|
+
element_count += 1
|
|
61
|
+
when REXML::CData
|
|
62
|
+
pruned.add(REXML::CData.new(truncate_text(child.value)))
|
|
63
|
+
when REXML::Text
|
|
64
|
+
text = truncate_text(child.value)
|
|
65
|
+
pruned.add_text(text) unless text.empty?
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
pruned
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def self.render_element(element)
|
|
73
|
+
formatter = REXML::Formatters::Default.new
|
|
74
|
+
output = +""
|
|
75
|
+
formatter.write(element, output)
|
|
76
|
+
output
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def self.compact_fallback(root_name)
|
|
80
|
+
"<#{root_name}><truncated/></#{root_name}>"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def self.truncate_text(text, max: MAX_TEXT)
|
|
84
|
+
value = text.to_s
|
|
85
|
+
return value if value.bytesize <= max
|
|
86
|
+
|
|
87
|
+
value.byteslice(0, max - TEXT_SUFFIX.bytesize) + TEXT_SUFFIX
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "logger"
|
|
4
|
+
require "rack"
|
|
5
|
+
|
|
6
|
+
require_relative "end_point_blank/access_tokens"
|
|
7
|
+
require_relative "end_point_blank/authorization"
|
|
8
|
+
require_relative "end_point_blank/version"
|
|
9
|
+
require_relative "end_point_blank/configuration"
|
|
10
|
+
require_relative "end_point_blank/session_configuration"
|
|
11
|
+
require_relative "end_point_blank/log_entry"
|
|
12
|
+
require_relative "end_point_blank/string_truncator"
|
|
13
|
+
require_relative "end_point_blank/fast_json_truncator"
|
|
14
|
+
require_relative "end_point_blank/xml_truncator"
|
|
15
|
+
require_relative "end_point_blank/masking"
|
|
16
|
+
require_relative "end_point_blank/writers/shared"
|
|
17
|
+
require_relative "end_point_blank/writers/delayed_writer"
|
|
18
|
+
require_relative "end_point_blank/writers/direct_writer"
|
|
19
|
+
require_relative "end_point_blank/writers/log_writer"
|
|
20
|
+
require_relative "end_point_blank/writers/exception_writer"
|
|
21
|
+
require_relative "end_point_blank/writers/request_writer"
|
|
22
|
+
require_relative "end_point_blank/writers/response_writer"
|
|
23
|
+
require_relative "end_point_blank/commands/generate_access_token"
|
|
24
|
+
require_relative "end_point_blank/commands/authentication_cache"
|
|
25
|
+
require_relative "end_point_blank/commands/basic_authenticate"
|
|
26
|
+
require_relative "end_point_blank/commands/bearer_generate"
|
|
27
|
+
require_relative "end_point_blank/commands/endpoint_authorize"
|
|
28
|
+
require_relative "end_point_blank/commands/route_pattern_finder"
|
|
29
|
+
require_relative "end_point_blank/commands/endpoint_update"
|
|
30
|
+
require_relative "end_point_blank/commands/version_finder"
|
|
31
|
+
require_relative "end_point_blank/middleware/rack/report_interaction"
|
|
32
|
+
require_relative "end_point_blank/rack/env_store"
|
|
33
|
+
require_relative "end_point_blank/rack/headers"
|
|
34
|
+
require_relative "end_point_blank/unauthorized_error"
|
|
35
|
+
if defined?(::Rails)
|
|
36
|
+
require_relative "end_point_blank/rails/authenticated"
|
|
37
|
+
require_relative "end_point_blank/rails/authorized"
|
|
38
|
+
require_relative "end_point_blank/rails/versioned"
|
|
39
|
+
require_relative "end_point_blank/rails/railtie"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
module EndPointBlank
|
|
43
|
+
class Error < StandardError; end
|
|
44
|
+
|
|
45
|
+
# Your code goes here...
|
|
46
|
+
def self.configure(&block)
|
|
47
|
+
yield Configuration.instance
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.logger
|
|
51
|
+
Configuration.instance.logger || (@default_logger ||= ::Logger.new($stdout, level: ::Logger::INFO))
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def self.logger=(logger)
|
|
55
|
+
Configuration.instance.logger = logger
|
|
56
|
+
end
|
|
57
|
+
end
|