Module: DebugBundle::RemoteConfig

Defined in:
lib/debugbundle/remote_config.rb

Defined Under Namespace

Classes: CapturePolicy, Directive, ImmediateClientErrorPathRule, Snapshot

Class Method Summary collapse

Class Method Details

.balanced_capture_policyObject



80
81
82
83
84
85
86
87
88
89
90
# File 'lib/debugbundle/remote_config.rb', line 80

def self.balanced_capture_policy
  CapturePolicy.new(
    preset: 'balanced',
    capture_logs: 'warning',
    capture_request_events: 'failures_only',
    capture_breadcrumbs: 'exception_only',
    capture_probe_events: 'buffer_only',
    immediate_client_error_statuses: [],
    immediate_client_error_path_rules: []
  )
end

.minimal_capture_policyObject



68
69
70
71
72
73
74
75
76
77
78
# File 'lib/debugbundle/remote_config.rb', line 68

def self.minimal_capture_policy
  CapturePolicy.new(
    preset: 'minimal',
    capture_logs: 'error',
    capture_request_events: 'failures_only',
    capture_breadcrumbs: 'local_only',
    capture_probe_events: 'buffer_only',
    immediate_client_error_statuses: [],
    immediate_client_error_path_rules: []
  )
end

.parse(payload, fallback_poll_interval_seconds) ⇒ Object



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/debugbundle/remote_config.rb', line 92

def self.parse(payload, fallback_poll_interval_seconds)
  return nil unless payload.is_a?(Hash)

  capture_policy =
    parse_capture_policy(payload['capture_policy'] || payload[:capture_policy]) || balanced_capture_policy
  directives = Array(payload['active_probes'] || payload[:active_probes]).filter_map do |entry|
    parse_directive(entry)
  end

  poll_interval_ms = payload['poll_interval_ms'] || payload[:poll_interval_ms]
  poll_interval_seconds = if poll_interval_ms.to_i.positive?
                            [(poll_interval_ms.to_i / 1000), 1].max
                          else
                            fallback_poll_interval_seconds
                          end

  Snapshot.new(
    probes_enabled: payload['probes_enabled'] != false && payload[:probes_enabled] != false,
    remote_probes_enabled: payload['remote_probes_enabled'] == true || payload[:remote_probes_enabled] == true,
    directives: directives,
    poll_interval_seconds: poll_interval_seconds,
    capture_policy: capture_policy,
    trigger_token_key: payload['trigger_token_key'] || payload[:trigger_token_key]
  )
end

.parse_capture_policy(payload) ⇒ Object



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/debugbundle/remote_config.rb', line 118

def self.parse_capture_policy(payload)
  return nil unless payload.is_a?(Hash)

  CapturePolicy.new(
    preset: payload['preset'] || payload[:preset] || 'balanced',
    capture_logs: payload['capture_logs'] || payload[:capture_logs] || 'warning',
    capture_request_events:
      payload['capture_request_events'] || payload[:capture_request_events] || 'failures_only',
    capture_breadcrumbs: payload['capture_breadcrumbs'] || payload[:capture_breadcrumbs] || 'exception_only',
    capture_probe_events: payload['capture_probe_events'] || payload[:capture_probe_events] || 'buffer_only',
    immediate_client_error_statuses: Array(
      payload['immediate_client_error_statuses'] || payload[:immediate_client_error_statuses]
    ).grep(Integer),
    immediate_client_error_path_rules: parse_immediate_client_error_path_rules(
      payload['immediate_client_error_path_rules'] || payload[:immediate_client_error_path_rules]
    )
  )
end

.parse_directive(payload) ⇒ Object



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/debugbundle/remote_config.rb', line 172

def self.parse_directive(payload)
  return nil unless payload.is_a?(Hash)

  expires_at_value = payload['expires_at'] || payload[:expires_at]
  expires_at = parse_time(expires_at_value)
  return nil unless expires_at

  Directive.new(
    id: payload['id'] || payload[:id],
    label_pattern: payload['label_pattern'] || payload[:label_pattern],
    service: payload['service'] || payload[:service] || '*',
    environment: payload['environment'] || payload[:environment] || '*',
    expires_at: expires_at
  )
end

.parse_immediate_client_error_path_rules(value) ⇒ Object



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/debugbundle/remote_config.rb', line 137

def self.parse_immediate_client_error_path_rules(value)
  entries = Array(value)
  return [] if entries.empty? || entries.length > 25

  entries.filter_map do |entry|
    next unless entry.is_a?(Hash)

    status_code = entry['status_code'] || entry[:status_code]
    path_pattern = entry['path_pattern'] || entry[:path_pattern]
    raw_methods = Array(entry['methods'] || entry[:methods])
    next unless status_code.is_a?(Integer) && (400..499).cover?(status_code)
    next unless valid_path_pattern?(path_pattern)
    next if raw_methods.length > 7

    http_methods = raw_methods.map { |method| method.to_s.upcase }.uniq
    next unless http_methods.all? { |method| %w[GET POST PUT PATCH DELETE HEAD OPTIONS].include?(method) }

    ImmediateClientErrorPathRule.new(
      status_code: status_code,
      path_pattern: path_pattern,
      http_methods: http_methods
    )
  end
end

.parse_time(value) ⇒ Object



188
189
190
191
192
193
194
# File 'lib/debugbundle/remote_config.rb', line 188

def self.parse_time(value)
  return nil unless value.is_a?(String) && !value.empty?

  Time.iso8601(value)
rescue ArgumentError
  nil
end

.valid_path_pattern?(value) ⇒ Boolean

Returns:

  • (Boolean)


162
163
164
165
166
167
168
169
170
# File 'lib/debugbundle/remote_config.rb', line 162

def self.valid_path_pattern?(value)
  return false unless value.is_a?(String)
  return false if value.empty? || value.length > 256
  return false unless value.start_with?('/')
  return false if value.include?('?') || value.include?('#')

  wildcard_index = value.index('*')
  wildcard_index.nil? || wildcard_index == value.length - 1
end