tsdb_time_series 4.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +22 -0
- data/.rubocop.yml +17 -0
- data/.rvmrc +1 -0
- data/CHANGELOG.md +38 -0
- data/CONTRIBUTING.md +22 -0
- data/Gemfile +4 -0
- data/Guardfile +27 -0
- data/README.md +232 -0
- data/Rakefile +44 -0
- data/lib/time_series.rb +7 -0
- data/lib/time_series/metric.rb +56 -0
- data/lib/time_series/query.rb +170 -0
- data/lib/time_series/results/result.rb +40 -0
- data/lib/time_series/results/synthetic_result.rb +103 -0
- data/lib/time_series/ts_client.rb +169 -0
- data/lib/time_series/version.rb +9 -0
- data/spec/acceptance/lib/time_series/metric_spec.rb +27 -0
- data/spec/acceptance/lib/time_series/synthetic_result_spec.rb +27 -0
- data/spec/acceptance/lib/time_series/ts_client_spec.rb +146 -0
- data/spec/fixtures.rb +17 -0
- data/spec/fixtures/errors/no_metric.json +6 -0
- data/spec/fixtures/errors/no_tag_key.json +6 -0
- data/spec/fixtures/query/sys.numa.allocation.json +17 -0
- data/spec/fixtures/query/sys.numa.allocation.rate.json +17 -0
- data/spec/fixtures/query/sys.numa.zoneallocs.json +17 -0
- data/spec/fixtures/suggest/sys.json +1 -0
- data/spec/integration/lib/time_series/integration_spec.rb +129 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/unit/lib/time_series/metric_spec.rb +48 -0
- data/spec/unit/lib/time_series/query_spec.rb +49 -0
- data/spec/unit/lib/time_series/synthetic_result_spec.rb +39 -0
- data/spec/unit/lib/time_series/ts_client_spec.rb +70 -0
- data/tsdb_time_series.gemspec +38 -0
- data/tsdb_time_series.reek +0 -0
- metadata +323 -0
@@ -0,0 +1,170 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module Opower
|
4
|
+
module TimeSeries
|
5
|
+
# Represents a query that can be sent to an OpenTSDB instance through a [TSDBClient] object.
|
6
|
+
class Query
|
7
|
+
attr_accessor :metrics, :request, :response, :format
|
8
|
+
|
9
|
+
# Creates a new Query object.
|
10
|
+
#
|
11
|
+
# @param [Hash] config The configuration for this query.
|
12
|
+
# @option config [String] :format The format to return data with. Defaults to 'json'.
|
13
|
+
# @option config [String, Integer, DateTime] :start The start time. Required field.
|
14
|
+
# @option config [String, Integer, DateTime] :end The end time. Optional field.
|
15
|
+
# @option config [Hash] :m Array of metric hashes. This maps to what OpenTSDB expects as the metrics to query.
|
16
|
+
# * :aggregator [String] The aggregation type to utilize. Optional. Defaults to 'sum' if omitted.
|
17
|
+
# * :metric [String] The metric name. Required.
|
18
|
+
# * :tags [Hash] Hash consisting of Tag Key / Tag Value pairs. Optional.
|
19
|
+
# * :down_sample [Hash] to specify downsampling period and function
|
20
|
+
# * :period [String] The period of time to downsample one
|
21
|
+
# * :function [String] The function [min, max, sum, avg, dev]
|
22
|
+
# @option config [Boolean] padding If set to true, OpenTSDB (>= 2.0) will pad the start/end period.
|
23
|
+
#
|
24
|
+
# This object also supports all of the options available to the REST API for OpenTSDB.
|
25
|
+
# See http://opentsdb.net/http-api.html#/q_Parameters for more information.
|
26
|
+
#
|
27
|
+
# @return [Query] new Query object
|
28
|
+
def initialize(config = {})
|
29
|
+
@request = config
|
30
|
+
@format = config.delete(:format)
|
31
|
+
|
32
|
+
# Check that 'start' and 'm' parameters required by OpenTSDB are present
|
33
|
+
@requirements = [:start, :m]
|
34
|
+
validate_metrics
|
35
|
+
|
36
|
+
@metrics = @request.delete(:m)
|
37
|
+
check_metrics
|
38
|
+
convert_dates
|
39
|
+
|
40
|
+
# Create 'm' array - this is the
|
41
|
+
@request[:m] = @metrics.map(&MetricQuery.method(:new))
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns the current query as a URL to a PNG generated by OpenTSDB
|
45
|
+
#
|
46
|
+
# @return [String] url to gnuplot graph
|
47
|
+
def as_graph
|
48
|
+
GraphingRequest.new(@request).to_s
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
# Validates the query for the configured required fields. OpenTSDB requires that at the minimum the start time
|
54
|
+
# and a metric field (defined by 'm') to be present.
|
55
|
+
#
|
56
|
+
# @raise [ArgumentError] thrown if 'm' or 'start' are missing
|
57
|
+
def validate_metrics
|
58
|
+
keys = @request.keys
|
59
|
+
@requirements.each do |req|
|
60
|
+
next if keys.include?(req.to_sym) || keys.include?(req.to_s)
|
61
|
+
fail(ArgumentError, "#{req} is a required parameter.")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Converts dates to a format that OpenTSDB understands.
|
66
|
+
def convert_dates
|
67
|
+
@request = Hash[@request.map do |key, value|
|
68
|
+
value = value.strftime('%Y/%m/%d-%H:%M:%S') if value.respond_to? :strftime
|
69
|
+
[key.to_s, value]
|
70
|
+
end]
|
71
|
+
end
|
72
|
+
|
73
|
+
# Checks the consistency of the metrics provided as defined in the user guide.
|
74
|
+
#
|
75
|
+
# @raise [ArgumentError] thrown if 'm' is in an unexpected state
|
76
|
+
def check_metrics
|
77
|
+
fail(ArgumentError, 'm parameter must be an array.') unless @metrics.is_a? Array
|
78
|
+
fail(ArgumentError, 'm parameter must not be empty.') unless @metrics.length > 0
|
79
|
+
|
80
|
+
@metrics.each do |metric|
|
81
|
+
fail(ArgumentError, "Expected a Hash - got a #{metric.class}: '#{metric}'") unless metric.is_a? Hash
|
82
|
+
fail(ArgumentError, 'Metric label must be present for query to run.') unless metric.key?(:metric)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Wrapper class for each query made against a separate metric.
|
87
|
+
class MetricQuery
|
88
|
+
# Initializes a wrapper object that represent a single metric that will be queried against in OpenTSDB.
|
89
|
+
#
|
90
|
+
# @param [Hash] metric a Hash representing a query against OpenTSDB
|
91
|
+
def initialize(metric)
|
92
|
+
@metric = metric.fetch(:metric)
|
93
|
+
@tags = metric.fetch(:tags, {})
|
94
|
+
@aggregator = metric.fetch(:aggregator, 'sum')
|
95
|
+
@rate = metric.fetch(:rate, false)
|
96
|
+
|
97
|
+
initialize_downsample(metric)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Builds the query string representation of this MetricQuery object.
|
101
|
+
#
|
102
|
+
# @return [String] the URL query string that will be used for this metric query.
|
103
|
+
def to_s
|
104
|
+
str = "#{@aggregator}:"
|
105
|
+
str << "#{@period}-#{@function}:" if @downsample
|
106
|
+
str << 'rate:' if @rate
|
107
|
+
str << "#{@metric}{#{build_tags}}"
|
108
|
+
str
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
# Initializes the down-sample setting on the metric
|
114
|
+
#
|
115
|
+
# @param [Hash] metric the metric hash
|
116
|
+
def initialize_downsample(metric)
|
117
|
+
@downsample = false
|
118
|
+
return unless metric.key?(:downsample)
|
119
|
+
|
120
|
+
down_sample = metric[:downsample]
|
121
|
+
@period = down_sample[:period]
|
122
|
+
@function = down_sample[:function]
|
123
|
+
@downsample = true
|
124
|
+
end
|
125
|
+
|
126
|
+
# Builds the string representation of the tags for the metric
|
127
|
+
#
|
128
|
+
# @return query string for tag metrics
|
129
|
+
def build_tags
|
130
|
+
@tags.map { |key, value| "#{key}=#{value}" }.join(',')
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Generates a graphing request URL from a properly configured Query object.
|
135
|
+
class GraphingRequest
|
136
|
+
# Initializes the graphing request wrapper using the data found in the Query object.
|
137
|
+
#
|
138
|
+
# @param [Hash] request query configuration
|
139
|
+
def initialize(request)
|
140
|
+
@parameters = request
|
141
|
+
@request = []
|
142
|
+
build_request
|
143
|
+
end
|
144
|
+
|
145
|
+
# Returns the URL for the specified query and its requested data.
|
146
|
+
#
|
147
|
+
# @return [String] URI address for the specified query
|
148
|
+
def to_s
|
149
|
+
URI.encode(@request.join('&'))
|
150
|
+
end
|
151
|
+
|
152
|
+
private
|
153
|
+
|
154
|
+
# Builds the query string for the OpenTSDB REST API.
|
155
|
+
# This method smells of :reek:NestedIterators
|
156
|
+
#
|
157
|
+
# @return [String] the GET query string for this object.
|
158
|
+
def build_request
|
159
|
+
@parameters.each_pair do |key, value|
|
160
|
+
if value.respond_to? :each
|
161
|
+
value.each { |array_element| @request << "#{key}=#{array_element.to_s.strip}" }
|
162
|
+
else
|
163
|
+
@request << "#{key}=#{value.to_s.strip}"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module Opower
|
4
|
+
module TimeSeries
|
5
|
+
# Wraps the OpenTSDB result with response codes and result counts
|
6
|
+
class Result
|
7
|
+
attr_reader :status, :length, :results, :error_message
|
8
|
+
|
9
|
+
# Takes the Excon response from OpenTSDB and parses it into the desired format.
|
10
|
+
#
|
11
|
+
# @param [Response] response The Excon response object
|
12
|
+
def initialize(response)
|
13
|
+
@status = response.status
|
14
|
+
@length = 0
|
15
|
+
data = response.body
|
16
|
+
|
17
|
+
parse_results(data)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Checks if the status code is not a 2XX HTTP response code.
|
21
|
+
#
|
22
|
+
# @return [Boolean] true if an error occurred
|
23
|
+
def errors?
|
24
|
+
@status.to_s !~ /^2/
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
# Parses the results from OpenTSDB
|
30
|
+
#
|
31
|
+
# @param [String] data HTTP Response Body
|
32
|
+
def parse_results(data)
|
33
|
+
@results = JSON.parse(data)
|
34
|
+
@length = @results.length
|
35
|
+
|
36
|
+
@error_message = @results['error']['message'] if errors? && @length > 0
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'dentaku'
|
4
|
+
|
5
|
+
module Opower
|
6
|
+
module TimeSeries
|
7
|
+
# Provides support for synthetic metrics
|
8
|
+
class SyntheticResult
|
9
|
+
attr_reader :results, :data
|
10
|
+
|
11
|
+
# Initializes a synthetic results wrapper and runs the calculations on the results data.
|
12
|
+
#
|
13
|
+
# @param name [String] - alias for this synthetic series
|
14
|
+
# @param formula [String] - the formula used to calculate data together
|
15
|
+
# @param data [Hash] - a hash containing key mappings to results to be used in the formula
|
16
|
+
def initialize(name, formula, data)
|
17
|
+
@name = name
|
18
|
+
@formula = formula
|
19
|
+
@data = data
|
20
|
+
@results = {}
|
21
|
+
@calculator = TimeSeriesCalculator.new
|
22
|
+
|
23
|
+
calculate
|
24
|
+
end
|
25
|
+
|
26
|
+
# Calculates the result of the formula set for this synthetic result. Currently, the timestamps
|
27
|
+
# of each of the queries must match in order for a calculation to be performed.
|
28
|
+
def calculate
|
29
|
+
formula_map = FormulaMap.new(@data)
|
30
|
+
|
31
|
+
formula_map.parameters.each do |ts, parameter|
|
32
|
+
@results[ts] = @calculator.evaluate(@formula, parameter)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Gives the total results size after calculating synthetic metrics.
|
37
|
+
#
|
38
|
+
# @return [Integer] the number of data-points
|
39
|
+
def length
|
40
|
+
@results.keys.length
|
41
|
+
end
|
42
|
+
|
43
|
+
# Subclass of Dentaku's calculator - adds math functions by default
|
44
|
+
class TimeSeriesCalculator < Dentaku::Calculator
|
45
|
+
def initialize
|
46
|
+
super
|
47
|
+
initialize_math_functions
|
48
|
+
end
|
49
|
+
|
50
|
+
# Initializes math functions provided by Ruby and places them into the calculator as functions.
|
51
|
+
# NOTE: You must wrap nested mathematical expressions in formulas or Dentaku will attempt to pass them
|
52
|
+
# as separate arguments into the lambda below!
|
53
|
+
# This method smells of :reek:NestedIterators
|
54
|
+
#
|
55
|
+
# For example:
|
56
|
+
# Assume x = 1, y = 2
|
57
|
+
# cos(x + y) is translated into cos(1, 'add', 2) - this calls Math.cos(1, 'add', 2)
|
58
|
+
# cos((x + y)) is translated into cos(3) - this correctly calls Math.cos(3)
|
59
|
+
def initialize_math_functions
|
60
|
+
Math.methods(false).each do |method|
|
61
|
+
math_method = Math.method(method)
|
62
|
+
add_function(
|
63
|
+
name: method,
|
64
|
+
type: :numeric,
|
65
|
+
signature: [:numeric],
|
66
|
+
body: ->(*args) { math_method.call(*args) }
|
67
|
+
)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Merges all matching data point timestamps together and assigns their values to formula keys
|
73
|
+
class FormulaMap
|
74
|
+
attr_reader :parameters
|
75
|
+
|
76
|
+
# Initializes the formula map using the set of results received from OpenTSDB.
|
77
|
+
#
|
78
|
+
# @param [Hash] data A hash mapping formula parameters to OpenTSDB results
|
79
|
+
def initialize(data)
|
80
|
+
@data = data
|
81
|
+
@parameters = {}
|
82
|
+
|
83
|
+
@data[@data.keys.sample].each_key do |ts|
|
84
|
+
build_hash(ts)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Checks each of the keys in the data provided to see if they contain a data-point at the
|
89
|
+
# specified timestamp. Does nothing if any of the keys is missing a data-point.
|
90
|
+
#
|
91
|
+
# @param [String] ts the OpenTSDB timestamp to check (yes, it's a String from OpenTSDB)
|
92
|
+
def build_hash(ts)
|
93
|
+
result = @data.map { |key, dps| { key => dps[ts] } if dps.key?(ts) }
|
94
|
+
return if result.include?(nil)
|
95
|
+
|
96
|
+
@parameters[ts] = result.reduce do |formula_map, parameter|
|
97
|
+
formula_map.merge(parameter)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'excon'
|
5
|
+
require 'json'
|
6
|
+
require 'logger'
|
7
|
+
|
8
|
+
module Opower
|
9
|
+
module TimeSeries
|
10
|
+
# Ruby client object to interface with an OpenTSDB instance.
|
11
|
+
class TSClient
|
12
|
+
attr_accessor :host, :port, :client, :config, :connection, :connection_settings
|
13
|
+
|
14
|
+
# Creates a connection to a specified OpenTSDB instance
|
15
|
+
#
|
16
|
+
# @param [String] host The hostname/IP to connect to. Defaults to 'localhost'.
|
17
|
+
# @param [Integer] port The port to connect to. Defaults to 4242.
|
18
|
+
# @param [String] protocol The protocol to use. Defaults to 'http'.
|
19
|
+
#
|
20
|
+
# @return [TSClient] Client connection to OpenTSDB.
|
21
|
+
def initialize(host = '127.0.0.1', port = 4242, protocol = 'http')
|
22
|
+
@host = host
|
23
|
+
@port = port
|
24
|
+
|
25
|
+
@client = "#{protocol}://#{host}:#{port}/"
|
26
|
+
@connection = Excon.new(@client, persistent: true, idempotent: true, tcp_nodelay: true)
|
27
|
+
@connection_settings = @connection.data
|
28
|
+
configure
|
29
|
+
end
|
30
|
+
|
31
|
+
# Configures client-specific options
|
32
|
+
#
|
33
|
+
# @param [Hash] cfg The configuration options to set.
|
34
|
+
# @option cfg [Boolean] :dry_run When set to true, the client does not actually read/write to OpenTSDB.
|
35
|
+
# @option cfg [String] :version The version of OpenTSDB to run against. Defaults to 2.0.
|
36
|
+
def configure(cfg = {})
|
37
|
+
@config = { dry_run: false, version: '2.0' }
|
38
|
+
@valid_config_keys = @config.keys
|
39
|
+
|
40
|
+
cfg.each do |key, value|
|
41
|
+
key = key.to_sym
|
42
|
+
@config[key] = value if @valid_config_keys.include?(key)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Basic check to see if the OpenTSDB is reachable
|
47
|
+
#
|
48
|
+
# @return [Boolean] true if call against api/version resolves
|
49
|
+
def valid?
|
50
|
+
@connection.get(path: 'api/version')
|
51
|
+
true
|
52
|
+
rescue Excon::Errors::SocketError, Excon::Errors::Timeout
|
53
|
+
false
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns suggestions for metric or tag names
|
57
|
+
#
|
58
|
+
# @param [String] query The string to search for
|
59
|
+
# @param [String] type The type to search for: 'metrics', 'tagk', 'tagv'
|
60
|
+
#
|
61
|
+
# @return [Array] an array of possible values based on the query/type
|
62
|
+
def suggest(query, type = 'metrics', max = 25)
|
63
|
+
return suggest_uri(query, type, max) if @config[:dry_run]
|
64
|
+
JSON.parse(@connection.get(path: 'api/suggest', query: { type: type, q: query, max: max }).body)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns the full URI for the suggest query in the context of this client.
|
68
|
+
#
|
69
|
+
# @param [String] query The string to search for
|
70
|
+
# @param [String] type The type to search for: 'metrics', 'tagk', 'tagv'
|
71
|
+
# @return [String] the URI
|
72
|
+
def suggest_uri(query, type = 'metrics', max = 25)
|
73
|
+
@client + "api/suggest?type=#{type}&q=#{query}&max=#{max}"
|
74
|
+
end
|
75
|
+
|
76
|
+
# Writes the specified Metric object to OpenTSDB.
|
77
|
+
#
|
78
|
+
# @param [Metric] metric The metric to write to OpenTSDB
|
79
|
+
def write(metric)
|
80
|
+
cmd = "echo \"put #{metric}\" | nc -w 30 #{@host} #{@port}"
|
81
|
+
|
82
|
+
if @config[:dry_run]
|
83
|
+
cmd
|
84
|
+
else
|
85
|
+
# Write into the db
|
86
|
+
ret = system(cmd)
|
87
|
+
|
88
|
+
# Command failed to run
|
89
|
+
fail(IOError, "Failed to insert metric #{metric.name} with value of #{metric.value} into OpenTSDB.") unless ret
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Runs the specified query against OpenTSDB. If config[:dry-run] is set to true or PNG format requested,
|
94
|
+
# it will only return the URL for the query. Otherwise it will return a Result object.
|
95
|
+
#
|
96
|
+
# @param [Query] query The query object to execute with.
|
97
|
+
# @return [Result || String] the results of the query
|
98
|
+
def run_query(query)
|
99
|
+
return query_uri(query) if @config[:dry_run] || query.format == :png
|
100
|
+
Result.new(@connection.get(path: 'api/query', query: query.request))
|
101
|
+
end
|
102
|
+
|
103
|
+
# Returns the full URI for the query in the context of this client.
|
104
|
+
#
|
105
|
+
# @param [Query] query The query object
|
106
|
+
# @return [String] the URI
|
107
|
+
def query_uri(query)
|
108
|
+
@client + 'api/query?' + query.as_graph
|
109
|
+
end
|
110
|
+
|
111
|
+
# Runs a synthetic query using queries against OpenTSDB. It expects a formula and a matching Hash which maps
|
112
|
+
# parameters in the formula to time_series' query objects.
|
113
|
+
#
|
114
|
+
# @param name [String] the alias for this synthetic series
|
115
|
+
# @param formula [String] the Dentaku calculator formula to use
|
116
|
+
# @param query_hash [Hash] a Hash containing Query objects that map to corresponding parameters in the formula
|
117
|
+
# @return [SyntheticResult] the calculated result of the formula
|
118
|
+
def run_synthetic_query(name, formula, query_hash)
|
119
|
+
results_hash = query_hash.map { |key, query| { key => run_query(query).results[0].fetch('dps') } }
|
120
|
+
results_hash = results_hash.reduce do |results, result|
|
121
|
+
results.merge(result)
|
122
|
+
end
|
123
|
+
|
124
|
+
SyntheticResult.new(name, formula, results_hash)
|
125
|
+
end
|
126
|
+
|
127
|
+
# Runs the specified queries against OpenTSDB in a HTTP pipelined connection.
|
128
|
+
#
|
129
|
+
# @param [Array] queries An array of queries to run against OpenTSDB.
|
130
|
+
# @return [Array] a matching array of results for each query
|
131
|
+
def run_queries(queries)
|
132
|
+
# requests cannot be idempotent when pipelined, so we temporarily disable it
|
133
|
+
@connection_settings[:idempotent] = false
|
134
|
+
|
135
|
+
results = run_pipelined_request(queries)
|
136
|
+
|
137
|
+
@connection_settings[:idempotent] = true
|
138
|
+
results
|
139
|
+
end
|
140
|
+
|
141
|
+
private
|
142
|
+
|
143
|
+
# Runs a series of queries in a pipelined, persistent HTTP request against OpenTSDB.
|
144
|
+
#
|
145
|
+
# @param [Array] queries Array of Query objects to run against OpenTSDB
|
146
|
+
# @return [Array] corresponding Array of Result objects
|
147
|
+
def run_pipelined_request(queries)
|
148
|
+
wrapper = PipelineWrapper.new(@config, queries)
|
149
|
+
responses = @connection.requests(wrapper.requests)
|
150
|
+
responses.map { |response| Result.new(response) }
|
151
|
+
end
|
152
|
+
|
153
|
+
# Wraps pipelined requests and creates their individual HTTP requests against OpenTSDB
|
154
|
+
class PipelineWrapper
|
155
|
+
attr_reader :requests
|
156
|
+
|
157
|
+
# Initializes the pipeline wrapper and sets up the Excon requests based on the queries.
|
158
|
+
#
|
159
|
+
# @param [Hash] config the client configuration
|
160
|
+
# @param [Array] queries the Array of Query objects to execute
|
161
|
+
def initialize(config, queries)
|
162
|
+
@config = config
|
163
|
+
@queries = queries
|
164
|
+
@requests = @queries.map { |query| { method: :get, path: 'api/query', query: query.request } }
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|