airbrake-ruby 4.8.0 → 5.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 +4 -4
- data/lib/airbrake-ruby.rb +132 -57
- data/lib/airbrake-ruby/async_sender.rb +7 -30
- data/lib/airbrake-ruby/backtrace.rb +8 -7
- data/lib/airbrake-ruby/benchmark.rb +1 -1
- data/lib/airbrake-ruby/code_hunk.rb +1 -1
- data/lib/airbrake-ruby/config.rb +59 -15
- data/lib/airbrake-ruby/config/processor.rb +71 -0
- data/lib/airbrake-ruby/config/validator.rb +9 -3
- data/lib/airbrake-ruby/deploy_notifier.rb +1 -1
- data/lib/airbrake-ruby/file_cache.rb +1 -1
- data/lib/airbrake-ruby/filter_chain.rb +16 -1
- data/lib/airbrake-ruby/filters/dependency_filter.rb +1 -0
- data/lib/airbrake-ruby/filters/exception_attributes_filter.rb +2 -2
- data/lib/airbrake-ruby/filters/gem_root_filter.rb +1 -0
- data/lib/airbrake-ruby/filters/git_last_checkout_filter.rb +5 -5
- data/lib/airbrake-ruby/filters/git_repository_filter.rb +3 -0
- data/lib/airbrake-ruby/filters/git_revision_filter.rb +2 -0
- data/lib/airbrake-ruby/filters/{keys_whitelist.rb → keys_allowlist.rb} +3 -3
- data/lib/airbrake-ruby/filters/{keys_blacklist.rb → keys_blocklist.rb} +3 -3
- data/lib/airbrake-ruby/filters/keys_filter.rb +39 -20
- data/lib/airbrake-ruby/filters/root_directory_filter.rb +1 -0
- data/lib/airbrake-ruby/filters/sql_filter.rb +7 -7
- data/lib/airbrake-ruby/filters/system_exit_filter.rb +1 -0
- data/lib/airbrake-ruby/filters/thread_filter.rb +5 -4
- data/lib/airbrake-ruby/grouppable.rb +12 -0
- data/lib/airbrake-ruby/ignorable.rb +1 -0
- data/lib/airbrake-ruby/inspectable.rb +2 -2
- data/lib/airbrake-ruby/loggable.rb +1 -1
- data/lib/airbrake-ruby/mergeable.rb +12 -0
- data/lib/airbrake-ruby/monotonic_time.rb +5 -0
- data/lib/airbrake-ruby/notice.rb +7 -14
- data/lib/airbrake-ruby/notice_notifier.rb +11 -3
- data/lib/airbrake-ruby/performance_breakdown.rb +16 -10
- data/lib/airbrake-ruby/performance_notifier.rb +80 -58
- data/lib/airbrake-ruby/promise.rb +1 -0
- data/lib/airbrake-ruby/query.rb +20 -15
- data/lib/airbrake-ruby/queue.rb +65 -0
- data/lib/airbrake-ruby/remote_settings.rb +105 -0
- data/lib/airbrake-ruby/remote_settings/callback.rb +44 -0
- data/lib/airbrake-ruby/remote_settings/settings_data.rb +116 -0
- data/lib/airbrake-ruby/request.rb +14 -12
- data/lib/airbrake-ruby/stat.rb +26 -33
- data/lib/airbrake-ruby/sync_sender.rb +3 -2
- data/lib/airbrake-ruby/tdigest.rb +43 -58
- data/lib/airbrake-ruby/thread_pool.rb +11 -1
- data/lib/airbrake-ruby/truncator.rb +10 -4
- data/lib/airbrake-ruby/version.rb +11 -1
- data/spec/airbrake_spec.rb +206 -71
- data/spec/async_sender_spec.rb +3 -12
- data/spec/backtrace_spec.rb +44 -44
- data/spec/code_hunk_spec.rb +11 -11
- data/spec/config/processor_spec.rb +143 -0
- data/spec/config/validator_spec.rb +23 -6
- data/spec/config_spec.rb +40 -14
- data/spec/deploy_notifier_spec.rb +2 -2
- data/spec/filter_chain_spec.rb +28 -1
- data/spec/filters/dependency_filter_spec.rb +1 -1
- data/spec/filters/gem_root_filter_spec.rb +9 -9
- data/spec/filters/git_last_checkout_filter_spec.rb +21 -4
- data/spec/filters/git_repository_filter.rb +1 -1
- data/spec/filters/git_revision_filter_spec.rb +10 -10
- data/spec/filters/{keys_whitelist_spec.rb → keys_allowlist_spec.rb} +29 -28
- data/spec/filters/{keys_blacklist_spec.rb → keys_blocklist_spec.rb} +39 -29
- data/spec/filters/root_directory_filter_spec.rb +9 -9
- data/spec/filters/sql_filter_spec.rb +58 -60
- data/spec/filters/system_exit_filter_spec.rb +1 -1
- data/spec/filters/thread_filter_spec.rb +32 -30
- data/spec/fixtures/project_root/code.rb +9 -9
- data/spec/loggable_spec.rb +17 -0
- data/spec/monotonic_time_spec.rb +11 -0
- data/spec/notice_notifier/options_spec.rb +17 -17
- data/spec/notice_notifier_spec.rb +20 -20
- data/spec/notice_spec.rb +6 -6
- data/spec/performance_breakdown_spec.rb +0 -1
- data/spec/performance_notifier_spec.rb +220 -73
- data/spec/query_spec.rb +1 -1
- data/spec/queue_spec.rb +18 -0
- data/spec/remote_settings/callback_spec.rb +143 -0
- data/spec/remote_settings/settings_data_spec.rb +348 -0
- data/spec/remote_settings_spec.rb +187 -0
- data/spec/request_spec.rb +1 -3
- data/spec/response_spec.rb +8 -8
- data/spec/spec_helper.rb +6 -6
- data/spec/stat_spec.rb +2 -12
- data/spec/sync_sender_spec.rb +14 -12
- data/spec/tdigest_spec.rb +7 -7
- data/spec/thread_pool_spec.rb +39 -10
- data/spec/timed_trace_spec.rb +1 -1
- data/spec/truncator_spec.rb +12 -12
- metadata +32 -14
@@ -5,24 +5,30 @@ module Airbrake
|
|
5
5
|
# @see Airbrake.notify_breakdown
|
6
6
|
# @api public
|
7
7
|
# @since v4.2.0
|
8
|
-
# rubocop:disable Metrics/
|
9
|
-
PerformanceBreakdown
|
10
|
-
:method, :route, :response_type, :groups, :start_time, :end_time
|
11
|
-
) do
|
8
|
+
# rubocop:disable Metrics/ParameterLists
|
9
|
+
class PerformanceBreakdown
|
12
10
|
include HashKeyable
|
13
11
|
include Ignorable
|
14
12
|
include Stashable
|
13
|
+
include Mergeable
|
14
|
+
|
15
|
+
attr_accessor :method, :route, :response_type, :groups, :timing, :time
|
15
16
|
|
16
17
|
def initialize(
|
17
18
|
method:,
|
18
19
|
route:,
|
19
20
|
response_type:,
|
20
21
|
groups:,
|
21
|
-
|
22
|
-
|
22
|
+
timing: nil,
|
23
|
+
time: Time.now
|
23
24
|
)
|
24
|
-
@
|
25
|
-
|
25
|
+
@time_utc = TimeTruncate.utc_truncate_minutes(time)
|
26
|
+
@method = method
|
27
|
+
@route = route
|
28
|
+
@response_type = response_type
|
29
|
+
@groups = groups
|
30
|
+
@timing = timing
|
31
|
+
@time = time
|
26
32
|
end
|
27
33
|
|
28
34
|
def destination
|
@@ -38,9 +44,9 @@ module Airbrake
|
|
38
44
|
'method' => method,
|
39
45
|
'route' => route,
|
40
46
|
'responseType' => response_type,
|
41
|
-
'time' => @
|
47
|
+
'time' => @time_utc,
|
42
48
|
}.delete_if { |_key, val| val.nil? }
|
43
49
|
end
|
44
50
|
end
|
45
|
-
# rubocop:enable Metrics/
|
51
|
+
# rubocop:enable Metrics/ParameterLists
|
46
52
|
end
|
@@ -4,6 +4,7 @@ module Airbrake
|
|
4
4
|
#
|
5
5
|
# @api public
|
6
6
|
# @since v3.2.0
|
7
|
+
# rubocop:disable Metrics/ClassLength
|
7
8
|
class PerformanceNotifier
|
8
9
|
include Inspectable
|
9
10
|
include Loggable
|
@@ -11,33 +12,29 @@ module Airbrake
|
|
11
12
|
def initialize
|
12
13
|
@config = Airbrake::Config.instance
|
13
14
|
@flush_period = Airbrake::Config.instance.performance_stats_flush_period
|
14
|
-
@
|
15
|
-
@
|
15
|
+
@async_sender = AsyncSender.new(:put)
|
16
|
+
@sync_sender = SyncSender.new(:put)
|
16
17
|
@schedule_flush = nil
|
17
|
-
@mutex = Mutex.new
|
18
18
|
@filter_chain = FilterChain.new
|
19
|
-
|
19
|
+
|
20
|
+
@payload = {}.extend(MonitorMixin)
|
21
|
+
@has_payload = @payload.new_cond
|
20
22
|
end
|
21
23
|
|
22
24
|
# @param [Hash] resource
|
23
25
|
# @see Airbrake.notify_query
|
24
26
|
# @see Airbrake.notify_request
|
25
27
|
def notify(resource)
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
promise = @config.check_performance_options(resource)
|
30
|
-
return promise if promise.rejected?
|
31
|
-
|
32
|
-
@filter_chain.refine(resource)
|
33
|
-
return if resource.ignored?
|
34
|
-
|
35
|
-
@mutex.synchronize do
|
36
|
-
update_payload(resource)
|
37
|
-
@flush_period > 0 ? schedule_flush : send(@payload, promise)
|
28
|
+
@payload.synchronize do
|
29
|
+
send_resource(resource, sync: false)
|
38
30
|
end
|
31
|
+
end
|
39
32
|
|
40
|
-
|
33
|
+
# @param [Hash] resource
|
34
|
+
# @since v4.10.0
|
35
|
+
# @see Airbrake.notify_queue_sync
|
36
|
+
def notify_sync(resource)
|
37
|
+
send_resource(resource, sync: true).value
|
41
38
|
end
|
42
39
|
|
43
40
|
# @see Airbrake.add_performance_filter
|
@@ -51,73 +48,97 @@ module Airbrake
|
|
51
48
|
end
|
52
49
|
|
53
50
|
def close
|
54
|
-
@
|
51
|
+
@payload.synchronize do
|
55
52
|
@schedule_flush.kill if @schedule_flush
|
56
|
-
@
|
53
|
+
@async_sender.close
|
57
54
|
logger.debug("#{LOG_LABEL} performance notifier closed")
|
58
55
|
end
|
59
56
|
end
|
60
57
|
|
61
58
|
private
|
62
59
|
|
63
|
-
def
|
64
|
-
@
|
65
|
-
|
60
|
+
def schedule_flush
|
61
|
+
@schedule_flush ||= Thread.new do
|
62
|
+
loop do
|
63
|
+
@payload.synchronize do
|
64
|
+
@last_flush_time ||= MonotonicTime.time_in_s
|
66
65
|
|
67
|
-
|
68
|
-
|
69
|
-
|
66
|
+
while (MonotonicTime.time_in_s - @last_flush_time) < @flush_period
|
67
|
+
@has_payload.wait(@flush_period)
|
68
|
+
end
|
69
|
+
|
70
|
+
if @payload.none?
|
71
|
+
@last_flush_time = nil
|
72
|
+
next
|
73
|
+
end
|
74
|
+
|
75
|
+
send(@async_sender, @payload, Airbrake::Promise.new)
|
76
|
+
@payload.clear
|
77
|
+
end
|
78
|
+
end
|
70
79
|
end
|
71
80
|
end
|
72
81
|
|
73
|
-
def
|
74
|
-
|
82
|
+
def send_resource(resource, sync:)
|
83
|
+
promise = check_configuration(resource)
|
84
|
+
return promise if promise.rejected?
|
75
85
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
rescue ThreadError => exception
|
80
|
-
logger.error("#{LOG_LABEL}: error occurred while flushing: #{exception}")
|
81
|
-
end
|
86
|
+
@filter_chain.refine(resource)
|
87
|
+
if resource.ignored?
|
88
|
+
return Promise.new.reject("#{resource.class} was ignored by a filter")
|
82
89
|
end
|
83
90
|
|
84
|
-
|
91
|
+
update_payload(resource)
|
92
|
+
if sync || @flush_period == 0
|
93
|
+
send(@sync_sender, @payload, promise)
|
94
|
+
else
|
95
|
+
@has_payload.signal
|
96
|
+
schedule_flush
|
97
|
+
end
|
85
98
|
end
|
86
99
|
|
87
|
-
def
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
@waiting = false
|
94
|
-
end
|
95
|
-
|
96
|
-
sleep(@flush_period)
|
100
|
+
def update_payload(resource)
|
101
|
+
if (total_stat = @payload[resource])
|
102
|
+
@payload.key(total_stat).merge(resource)
|
103
|
+
else
|
104
|
+
@payload[resource] = { total: Airbrake::Stat.new }
|
105
|
+
end
|
97
106
|
|
98
|
-
|
99
|
-
@mutex.synchronize do
|
100
|
-
payload = @payload
|
101
|
-
@payload = {}
|
102
|
-
end
|
107
|
+
@payload[resource][:total].increment_ms(resource.timing)
|
103
108
|
|
104
|
-
|
105
|
-
|
109
|
+
resource.groups.each do |name, ms|
|
110
|
+
@payload[resource][name] ||= Airbrake::Stat.new
|
111
|
+
@payload[resource][name].increment_ms(ms)
|
106
112
|
end
|
107
113
|
end
|
108
114
|
|
109
|
-
def
|
110
|
-
|
111
|
-
|
115
|
+
def check_configuration(resource)
|
116
|
+
promise = @config.check_configuration
|
117
|
+
return promise if promise.rejected?
|
118
|
+
|
119
|
+
promise = @config.check_performance_options(resource)
|
120
|
+
return promise if promise.rejected?
|
112
121
|
|
113
|
-
|
122
|
+
if resource.timing && resource.timing == 0
|
123
|
+
return Promise.new.reject(':timing cannot be zero')
|
124
|
+
end
|
125
|
+
|
126
|
+
Promise.new
|
127
|
+
end
|
128
|
+
|
129
|
+
def send(sender, payload, promise)
|
130
|
+
raise "payload cannot be empty. Race?" if payload.none?
|
114
131
|
|
115
132
|
with_grouped_payload(payload) do |resource_hash, destination|
|
116
133
|
url = URI.join(
|
117
|
-
@config.
|
118
|
-
"api/v5/projects/#{@config.project_id}/#{destination}"
|
134
|
+
@config.apm_host,
|
135
|
+
"api/v5/projects/#{@config.project_id}/#{destination}",
|
119
136
|
)
|
120
|
-
|
137
|
+
|
138
|
+
logger.debug do
|
139
|
+
"#{LOG_LABEL} #{self.class.name}##{__method__}: #{resource_hash}"
|
140
|
+
end
|
141
|
+
sender.send(resource_hash, promise, url)
|
121
142
|
end
|
122
143
|
|
123
144
|
promise
|
@@ -152,4 +173,5 @@ module Airbrake
|
|
152
173
|
end
|
153
174
|
end
|
154
175
|
end
|
176
|
+
# rubocop:enable Metrics/ClassLength
|
155
177
|
end
|
data/lib/airbrake-ruby/query.rb
CHANGED
@@ -4,13 +4,15 @@ module Airbrake
|
|
4
4
|
# @see Airbrake.notify_query
|
5
5
|
# @api public
|
6
6
|
# @since v3.2.0
|
7
|
-
# rubocop:disable Metrics/ParameterLists
|
8
|
-
Query
|
9
|
-
:method, :route, :query, :func, :file, :line, :start_time, :end_time
|
10
|
-
) do
|
7
|
+
# rubocop:disable Metrics/ParameterLists
|
8
|
+
class Query
|
11
9
|
include HashKeyable
|
12
10
|
include Ignorable
|
13
11
|
include Stashable
|
12
|
+
include Mergeable
|
13
|
+
include Grouppable
|
14
|
+
|
15
|
+
attr_accessor :method, :route, :query, :func, :file, :line, :timing, :time
|
14
16
|
|
15
17
|
def initialize(
|
16
18
|
method:,
|
@@ -19,11 +21,18 @@ module Airbrake
|
|
19
21
|
func: nil,
|
20
22
|
file: nil,
|
21
23
|
line: nil,
|
22
|
-
|
23
|
-
|
24
|
+
timing: nil,
|
25
|
+
time: Time.now
|
24
26
|
)
|
25
|
-
@
|
26
|
-
|
27
|
+
@time_utc = TimeTruncate.utc_truncate_minutes(time)
|
28
|
+
@method = method
|
29
|
+
@route = route
|
30
|
+
@query = query
|
31
|
+
@func = func
|
32
|
+
@file = file
|
33
|
+
@line = line
|
34
|
+
@timing = timing
|
35
|
+
@time = time
|
27
36
|
end
|
28
37
|
|
29
38
|
def destination
|
@@ -34,21 +43,17 @@ module Airbrake
|
|
34
43
|
'queries'
|
35
44
|
end
|
36
45
|
|
37
|
-
def groups
|
38
|
-
{}
|
39
|
-
end
|
40
|
-
|
41
46
|
def to_h
|
42
47
|
{
|
43
48
|
'method' => method,
|
44
49
|
'route' => route,
|
45
50
|
'query' => query,
|
46
|
-
'time' => @
|
51
|
+
'time' => @time_utc,
|
47
52
|
'function' => func,
|
48
53
|
'file' => file,
|
49
|
-
'line' => line
|
54
|
+
'line' => line,
|
50
55
|
}.delete_if { |_key, val| val.nil? }
|
51
56
|
end
|
52
|
-
# rubocop:enable Metrics/ParameterLists
|
57
|
+
# rubocop:enable Metrics/ParameterLists
|
53
58
|
end
|
54
59
|
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Airbrake
|
2
|
+
# Queue represents a queue (worker).
|
3
|
+
#
|
4
|
+
# @see Airbrake.notify_queue
|
5
|
+
# @api public
|
6
|
+
# @since v4.9.0
|
7
|
+
class Queue
|
8
|
+
include HashKeyable
|
9
|
+
include Ignorable
|
10
|
+
include Stashable
|
11
|
+
|
12
|
+
attr_accessor :queue, :error_count, :groups, :timing, :time
|
13
|
+
|
14
|
+
def initialize(
|
15
|
+
queue:,
|
16
|
+
error_count:,
|
17
|
+
groups: {},
|
18
|
+
timing: nil,
|
19
|
+
time: Time.now
|
20
|
+
)
|
21
|
+
@time_utc = TimeTruncate.utc_truncate_minutes(time)
|
22
|
+
@queue = queue
|
23
|
+
@error_count = error_count
|
24
|
+
@groups = groups
|
25
|
+
@timing = timing
|
26
|
+
@time = time
|
27
|
+
end
|
28
|
+
|
29
|
+
def destination
|
30
|
+
'queues-stats'
|
31
|
+
end
|
32
|
+
|
33
|
+
def cargo
|
34
|
+
'queues'
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_h
|
38
|
+
{
|
39
|
+
'queue' => queue,
|
40
|
+
'errorCount' => error_count,
|
41
|
+
'time' => @time_utc,
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
def hash
|
46
|
+
{
|
47
|
+
'queue' => queue,
|
48
|
+
'time' => @time_utc,
|
49
|
+
}.hash
|
50
|
+
end
|
51
|
+
|
52
|
+
def merge(other)
|
53
|
+
self.error_count += other.error_count
|
54
|
+
end
|
55
|
+
|
56
|
+
# Queues don't have routes, but we want to define this to make sure our
|
57
|
+
# filter API is consistent (other models define this property)
|
58
|
+
#
|
59
|
+
# @return [String] empty route
|
60
|
+
# @see https://github.com/airbrake/airbrake-ruby/pull/537
|
61
|
+
def route
|
62
|
+
''
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module Airbrake
|
2
|
+
# RemoteSettings polls the remote config of the passed project at fixed
|
3
|
+
# intervals. The fetched config is yielded as a callback parameter so that the
|
4
|
+
# invoker can define read config values.
|
5
|
+
#
|
6
|
+
# @example Disable/enable error notifications based on the remote value
|
7
|
+
# RemoteSettings.poll do |data|
|
8
|
+
# config.error_notifications = data.error_notifications?
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# @since v5.0.0
|
12
|
+
# @api private
|
13
|
+
class RemoteSettings
|
14
|
+
include Airbrake::Loggable
|
15
|
+
|
16
|
+
# @return [Hash{Symbol=>String}] metadata to be attached to every GET
|
17
|
+
# request
|
18
|
+
QUERY_PARAMS = URI.encode_www_form(
|
19
|
+
notifier_name: Airbrake::NOTIFIER_INFO[:name],
|
20
|
+
notifier_version: Airbrake::NOTIFIER_INFO[:version],
|
21
|
+
os: RUBY_PLATFORM,
|
22
|
+
language: "#{RUBY_ENGINE}/#{RUBY_VERSION}".freeze,
|
23
|
+
).freeze
|
24
|
+
|
25
|
+
# @return [String]
|
26
|
+
HTTP_OK = '200'.freeze
|
27
|
+
|
28
|
+
# Polls remote config of the given project.
|
29
|
+
#
|
30
|
+
# @param [Integer] project_id
|
31
|
+
# @param [String] host
|
32
|
+
# @yield [data]
|
33
|
+
# @yieldparam data [Airbrake::RemoteSettings::SettingsData]
|
34
|
+
# @return [Airbrake::RemoteSettings]
|
35
|
+
def self.poll(project_id, host, &block)
|
36
|
+
new(project_id, host, &block).poll
|
37
|
+
end
|
38
|
+
|
39
|
+
# @param [Integer] project_id
|
40
|
+
# @yield [data]
|
41
|
+
# @yieldparam data [Airbrake::RemoteSettings::SettingsData]
|
42
|
+
def initialize(project_id, host, &block)
|
43
|
+
@data = SettingsData.new(project_id, {})
|
44
|
+
@host = host
|
45
|
+
@block = block
|
46
|
+
@poll = nil
|
47
|
+
end
|
48
|
+
|
49
|
+
# Polls remote config of the given project in background.
|
50
|
+
#
|
51
|
+
# @return [self]
|
52
|
+
def poll
|
53
|
+
@poll ||= Thread.new do
|
54
|
+
@block.call(@data)
|
55
|
+
|
56
|
+
loop do
|
57
|
+
@block.call(@data.merge!(fetch_config))
|
58
|
+
sleep(@data.interval)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
self
|
63
|
+
end
|
64
|
+
|
65
|
+
# Stops the background poller thread.
|
66
|
+
#
|
67
|
+
# @return [void]
|
68
|
+
def stop_polling
|
69
|
+
@poll.kill if @poll
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def fetch_config
|
75
|
+
response = nil
|
76
|
+
begin
|
77
|
+
response = Net::HTTP.get_response(build_config_uri)
|
78
|
+
rescue StandardError => ex
|
79
|
+
logger.error(ex)
|
80
|
+
return {}
|
81
|
+
end
|
82
|
+
|
83
|
+
unless response.code == HTTP_OK
|
84
|
+
logger.error(response.body)
|
85
|
+
return {}
|
86
|
+
end
|
87
|
+
|
88
|
+
json = nil
|
89
|
+
begin
|
90
|
+
json = JSON.parse(response.body)
|
91
|
+
rescue JSON::ParserError => ex
|
92
|
+
logger.error(ex)
|
93
|
+
return {}
|
94
|
+
end
|
95
|
+
|
96
|
+
json
|
97
|
+
end
|
98
|
+
|
99
|
+
def build_config_uri
|
100
|
+
uri = URI(@data.config_route(@host))
|
101
|
+
uri.query = QUERY_PARAMS
|
102
|
+
uri
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|