multiforecast-client 0.62.0.2
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/.gitignore +14 -0
- data/.pryrc +5 -0
- data/.rdebugrc +4 -0
- data/.rspec +2 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile +5 -0
- data/LICENSE +22 -0
- data/README.md +96 -0
- data/Rakefile +15 -0
- data/VERSION +1 -0
- data/bin/multiforecast +5 -0
- data/examples/example.rb +161 -0
- data/lib/multiforecast/cli.rb +69 -0
- data/lib/multiforecast/client.rb +326 -0
- data/lib/multiforecast/conversion_rule.rb +42 -0
- data/lib/multiforecast/shared_context/mock.rb +163 -0
- data/lib/multiforecast/shared_context/setup.rb +62 -0
- data/lib/multiforecast/shared_context.rb +22 -0
- data/lib/multiforecast/shared_examples.rb +30 -0
- data/lib/multiforecast-client.rb +2 -0
- data/multiforecast-client.gemspec +29 -0
- data/spec/multiforecast/client_spec.rb +170 -0
- data/spec/multiforecast/conversion_rule_spec.rb +152 -0
- data/spec/spec_helper.rb +18 -0
- metadata +170 -0
@@ -0,0 +1,326 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'growthforecast-client'
|
3
|
+
|
4
|
+
module MultiForecast
|
5
|
+
class Error < StandardError; end
|
6
|
+
class NotFound < Error; end
|
7
|
+
class AlreadyExists < Error; end
|
8
|
+
|
9
|
+
class Client
|
10
|
+
include ::MultiForecast::ConversionRule
|
11
|
+
attr_accessor :clients
|
12
|
+
attr_accessor :debug_dev
|
13
|
+
attr_accessor :short_metrics
|
14
|
+
|
15
|
+
# @param [Hash] opts
|
16
|
+
# [Hash] mapping: Mapping rules from `path` to GrowthForecast's `base_uri`.
|
17
|
+
def initialize(opts = {})
|
18
|
+
@mapping = opts['mapping'] || { '' => 'http://localhost:5125' }
|
19
|
+
@short_metrics = opts['short_metrics'] || true
|
20
|
+
|
21
|
+
@clients = {}
|
22
|
+
@base_uris = {}
|
23
|
+
@mapping.each do |path, base_uri|
|
24
|
+
if base_uri.kind_of?(Hash)
|
25
|
+
base_uri = uri['in_uri']
|
26
|
+
out_uri = uri['out_uri']
|
27
|
+
end
|
28
|
+
@clients[path] = GrowthForecast::Client.new(base_uri)
|
29
|
+
@base_uris[path] = out_uri || base_uri
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# set the `debug_dev` attribute of HTTPClient
|
34
|
+
# @param [IO] debug_dev such as STDOUT
|
35
|
+
def debug_dev=(debug_dev)
|
36
|
+
@debug_dev = debug_dev
|
37
|
+
@clients.each {|c| c.debug_dev = debug_dev }
|
38
|
+
end
|
39
|
+
|
40
|
+
def clients(base_path = nil)
|
41
|
+
base_path.nil? ? @clients.values : @clients.values_at(*ids(base_path)).compact
|
42
|
+
end
|
43
|
+
|
44
|
+
def client(path)
|
45
|
+
@last_client = @clients[id(path)]
|
46
|
+
end
|
47
|
+
|
48
|
+
def last_client
|
49
|
+
@last_client
|
50
|
+
end
|
51
|
+
|
52
|
+
def last_response
|
53
|
+
@last_client.last_response
|
54
|
+
end
|
55
|
+
|
56
|
+
# GET the JSON API
|
57
|
+
# @param [String] path
|
58
|
+
# @return [Hash] response body
|
59
|
+
def get_json(path)
|
60
|
+
client(path).get_json(path)
|
61
|
+
end
|
62
|
+
|
63
|
+
# POST the JSON API
|
64
|
+
# @param [String] path
|
65
|
+
# @param [Hash] data
|
66
|
+
# @return [Hash] response body
|
67
|
+
def post_json(path, data = {})
|
68
|
+
client(path).post_json(path, data)
|
69
|
+
end
|
70
|
+
|
71
|
+
# POST the non-JSON API
|
72
|
+
# @param [String] path
|
73
|
+
# @param [Hash] data
|
74
|
+
# @return [String] response body
|
75
|
+
def post_query(path, data = {})
|
76
|
+
client(path).post_query(path, data)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Get the list of graphs, /json/list/graph
|
80
|
+
# @return [Hash] list of graphs
|
81
|
+
# @example
|
82
|
+
# [
|
83
|
+
# {"base_uri"=>"xxxxx",
|
84
|
+
# "service_name"=>"mbclient",
|
85
|
+
# "section_name"=>"mbclient",
|
86
|
+
# "graph_name"=>"test%2Fhostname%2F%3C2sec_count",
|
87
|
+
# "path"=>"test/hostname/<2sec_count",
|
88
|
+
# "id"=>4},
|
89
|
+
# {"base_uri"=>"xxxxx",
|
90
|
+
# "service_name"=>"mbclient",
|
91
|
+
# "section_name"=>"mbclient",
|
92
|
+
# "graph_name"=>"test%2Fhostname%2F%3C1sec_count",
|
93
|
+
# "path"=>"test/hostname/<1sec_count",
|
94
|
+
# "id"=>3},
|
95
|
+
# ]
|
96
|
+
def list_graph(base_path = nil)
|
97
|
+
clients(base_path).inject([]) do |ret, client|
|
98
|
+
graphs = []
|
99
|
+
client.list_graph.each do |graph|
|
100
|
+
graph['base_uri'] = client.base_uri
|
101
|
+
graph['path'] = path(graph['service_name'], graph['section_name'], graph['graph_name'])
|
102
|
+
graphs << graph if base_path.nil? or graph['path'].index(base_path) == 0
|
103
|
+
end
|
104
|
+
ret = ret + graphs
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Get the propety of a graph, GET /api/:path
|
109
|
+
# @param [String] path
|
110
|
+
# @return [Hash] the graph property
|
111
|
+
# @example
|
112
|
+
#{
|
113
|
+
# "base_uri" => "xxxxxx",
|
114
|
+
# "path" => "test/hostname/<4sec_count",
|
115
|
+
# "service_name"=>"mbclient",
|
116
|
+
# "section_name"=>"mbclient",
|
117
|
+
# "graph_name"=>"test%2Fhostname%2F%3C4sec_count",
|
118
|
+
# "number"=>1,
|
119
|
+
# "llimit"=>-1000000000,
|
120
|
+
# "mode"=>"gauge",
|
121
|
+
# "stype"=>"AREA",
|
122
|
+
# "adjustval"=>"1",
|
123
|
+
# "meta"=>"",
|
124
|
+
# "gmode"=>"gauge",
|
125
|
+
# "color"=>"#cc6633",
|
126
|
+
# "created_at"=>"2013/02/02 00:41:11",
|
127
|
+
# "ulimit"=>1000000000,
|
128
|
+
# "id"=>21,
|
129
|
+
# "description"=>"",
|
130
|
+
# "sulimit"=>100000,
|
131
|
+
# "unit"=>"",
|
132
|
+
# "sort"=>0,
|
133
|
+
# "updated_at"=>"2013/02/02 02:32:10",
|
134
|
+
# "adjust"=>"*",
|
135
|
+
# "type"=>"AREA",
|
136
|
+
# "sllimit"=>-100000,
|
137
|
+
# "md5"=>"3c59dc048e8850243be8079a5c74d079"}
|
138
|
+
def get_graph(path)
|
139
|
+
client(path).get_graph(service_name(path), section_name(path), graph_name(path)).tap do |graph|
|
140
|
+
graph['base_uri'] = client(path).base_uri
|
141
|
+
graph['path'] = path
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Post parameters to a graph, POST /api/:path
|
146
|
+
# @param [String] path
|
147
|
+
# @param [Hash] params The POST parameters. See #get_graph
|
148
|
+
def post_graph(path, params)
|
149
|
+
client(path).post_graph(service_name(path), section_name(path), graph_name(path), params)
|
150
|
+
end
|
151
|
+
|
152
|
+
# Delete a graph, POST /delete/:path
|
153
|
+
# @param [String] path
|
154
|
+
def delete_graph(path)
|
155
|
+
client(path).delete_graph(service_name(path), section_name(path), graph_name(path))
|
156
|
+
end
|
157
|
+
|
158
|
+
# Update the property of a graph, /json/edit/graph/:id
|
159
|
+
# @param [String] path
|
160
|
+
# @param [Hash] params
|
161
|
+
# All of parameters given by #get_graph are available except `number` and `mode`.
|
162
|
+
# @return [Hash] error response
|
163
|
+
# @example
|
164
|
+
# {"error"=>0} #=> Success
|
165
|
+
# {"error"=>1} #=> Error
|
166
|
+
def edit_graph(path, params)
|
167
|
+
client(path).edit_graph(service_name(path), section_name(path), graph_name(path), params)
|
168
|
+
end
|
169
|
+
|
170
|
+
# Get the list of complex graphs, /json/list/complex
|
171
|
+
# @return [Hash] list of complex graphs
|
172
|
+
# @example
|
173
|
+
# [
|
174
|
+
# {"base_uri"=>"xxxxx",
|
175
|
+
# "path"=>"test/hostname/<2sec_count",
|
176
|
+
# "service_name"=>"mbclient",
|
177
|
+
# "section_name"=>"mbclient",
|
178
|
+
# "graph_name"=>"test%2Fhostname%2F%3C2sec_count",
|
179
|
+
# "id"=>4},
|
180
|
+
# {"base_uri"=>"xxxxx",
|
181
|
+
# "path"=>"test/hostname/<1sec_count",
|
182
|
+
# "service_name"=>"mbclient",
|
183
|
+
# "section_name"=>"mbclient",
|
184
|
+
# "graph_name"=>"test%2Fhostname%2F%3C1sec_count",
|
185
|
+
# "id"=>3},
|
186
|
+
# ]
|
187
|
+
def list_complex(base_path = nil)
|
188
|
+
clients(base_path).inject([]) do |ret, client|
|
189
|
+
graphs = []
|
190
|
+
client.list_complex.each do |graph|
|
191
|
+
graph['base_uri'] = client.base_uri
|
192
|
+
graph['path'] = path(graph['service_name'], graph['section_name'], graph['graph_name'])
|
193
|
+
graphs << graph if base_path.nil? or graph['path'].index(base_path) == 0
|
194
|
+
end
|
195
|
+
ret = ret + graphs
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# Create a complex graph
|
200
|
+
#
|
201
|
+
# @param [Array] from_graphs Array of graph properties whose keys are
|
202
|
+
# ["path", "gmode", "stack", "type"]
|
203
|
+
# @param [Hash] to_complex Property of Complex Graph, whose keys are like
|
204
|
+
# ["path", "description", "sort"]
|
205
|
+
def create_complex(from_graphs, to_complex)
|
206
|
+
from_graphs = from_graphs.dup
|
207
|
+
to_complex = to_complex.dup
|
208
|
+
|
209
|
+
from_graphs.each do |from_graph|
|
210
|
+
from_graph['service_name'] = service_name(from_graph['path'])
|
211
|
+
from_graph['section_name'] = section_name(from_graph['path'])
|
212
|
+
from_graph['graph_name'] = graph_name(from_graph['path'])
|
213
|
+
from_graph.delete('path')
|
214
|
+
from_graph.delete('base_uri')
|
215
|
+
end
|
216
|
+
|
217
|
+
to_complex['service_name'] = service_name(to_complex['path'])
|
218
|
+
to_complex['section_name'] = section_name(to_complex['path'])
|
219
|
+
to_complex['graph_name'] = graph_name(to_complex['path'])
|
220
|
+
path = to_complex.delete('path')
|
221
|
+
|
222
|
+
# NOTE: FROM_GRAPHS AND TO _COMPLEX MUST BE THE SAME GF SERVER
|
223
|
+
client(path).create_complex(from_graphs, to_complex)
|
224
|
+
end
|
225
|
+
|
226
|
+
# Get a complex graph
|
227
|
+
#
|
228
|
+
# @param [String] path
|
229
|
+
# @return [Hash] the graph property
|
230
|
+
# @example
|
231
|
+
# {"number"=>0,
|
232
|
+
# "complex"=>true,
|
233
|
+
# "created_at"=>"2013/05/20 15:08:28",
|
234
|
+
# "service_name"=>"app name",
|
235
|
+
# "section_name"=>"host name",
|
236
|
+
# "id"=>18,
|
237
|
+
# "graph_name"=>"complex graph test",
|
238
|
+
# "data"=>
|
239
|
+
# [{"gmode"=>"gauge", "stack"=>false, "type"=>"AREA", "graph_id"=>218},
|
240
|
+
# {"gmode"=>"gauge", "stack"=>true, "type"=>"AREA", "graph_id"=>217}],
|
241
|
+
# "sumup"=>false,
|
242
|
+
# "description"=>"complex graph test",
|
243
|
+
# "sort"=>10,
|
244
|
+
# "updated_at"=>"2013/05/20 15:08:28"}
|
245
|
+
def get_complex(path)
|
246
|
+
client(path).get_complex(service_name(path), section_name(path), graph_name(path)).tap do |graph|
|
247
|
+
graph['base_uri'] = client(path).base_uri
|
248
|
+
graph['path'] = path
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
# Delete a complex graph
|
253
|
+
#
|
254
|
+
# @param [String] path
|
255
|
+
# @return [Hash] error response
|
256
|
+
# @example
|
257
|
+
# {"error"=>0} #=> Success
|
258
|
+
# {"error"=>1} #=> Error
|
259
|
+
def delete_complex(path)
|
260
|
+
client(path).delete_complex(service_name(path), section_name(path), graph_name(path))
|
261
|
+
end
|
262
|
+
|
263
|
+
# Get graph image uri
|
264
|
+
#
|
265
|
+
# @param [String] path
|
266
|
+
# @param [Hash] params for the query string
|
267
|
+
# t [String] the time unit such as 'h' (an hour), '4h' (4 hours), '8h', 'n' (half day), 'd' (a day), '3d', 'w', (a week), 'm' (a month), 'y' (a year).
|
268
|
+
# Also, 'sh' 's4h' 's8h', 'sn', 'sd', 's3d' for graphs generated by short period GF worker.
|
269
|
+
# Also, this parameter is overrided with 'c' or 'sc' when `from` parameter is set.
|
270
|
+
# from [String|Time] the time period to show 'from'. String describing a time, or a Time object
|
271
|
+
# to [String|Time] the time period to show 'to'. String describing a time, or a Time Object
|
272
|
+
# width [String] the widh of image to show
|
273
|
+
# height [String] the height of image to show
|
274
|
+
# @return [Hash] error response
|
275
|
+
# @example
|
276
|
+
def get_graph_uri(path, params = {})
|
277
|
+
params = preprocess_time_params(params) if params
|
278
|
+
"#{@base_uris[id(path)]}/graph/#{CGI.escape(service_name(path))}/#{CGI.escape(section_name(path))}/#{CGI.escape(graph_name(path))}?#{query_string(params)}"
|
279
|
+
end
|
280
|
+
|
281
|
+
# Get complex graph image uri
|
282
|
+
#
|
283
|
+
# @param [String] path
|
284
|
+
# @param [Hash] params for the query string
|
285
|
+
# t [String] the time unit such as 'h' (an hour), '4h' (4 hours), '8h', 'n' (half day), 'd' (a day), '3d', 'w', (a week), 'm' (a month), 'y' (a year).
|
286
|
+
# Also, 'sh' 's4h' 's8h', 'sn', 'sd', 's3d' for graphs generated by short period GF worker.
|
287
|
+
# Also, this parameter is overrided with 'c' or 'sc' when `from` parameter is set.
|
288
|
+
# from [String|Time] the time period to show 'from'. String describing a time, or a Time object
|
289
|
+
# to [String|Time] the time period to show 'to'. String describing a time, or a Time Object
|
290
|
+
# width [String] the widh of image to show
|
291
|
+
# height [String] the height of image to show
|
292
|
+
# @return [Hash] error response
|
293
|
+
# @example
|
294
|
+
def get_complex_uri(path, params = {})
|
295
|
+
params = preprocess_time_params(params) if params
|
296
|
+
"#{@base_uris[id(path)]}/complex/graph/#{CGI.escape(service_name(path))}/#{CGI.escape(section_name(path))}/#{CGI.escape(graph_name(path))}?#{query_string(params)}"
|
297
|
+
end
|
298
|
+
|
299
|
+
# process the time params (from and to)
|
300
|
+
def preprocess_time_params(params)
|
301
|
+
params = params.dup
|
302
|
+
params['from'] = Time.parse(params['from']) if params['from'].kind_of?(String)
|
303
|
+
params['to'] = Time.parse(params['to']) if params['to'].kind_of?(String)
|
304
|
+
if params['from'] and params['to']
|
305
|
+
# if from is more future than 3 days ago, use 'sc' (short period time worker)
|
306
|
+
params['t'] = (@short_metrics && params['from'] > Time.now - 60 * 60 * 24 * 3) ? 'sc' : 'c'
|
307
|
+
params['from'] = params['from'].strftime("%F %T %z") # format is determined
|
308
|
+
params['to'] = params['to'].strftime("%F %T %z")
|
309
|
+
end
|
310
|
+
params
|
311
|
+
end
|
312
|
+
|
313
|
+
private
|
314
|
+
|
315
|
+
# build URI query string
|
316
|
+
#
|
317
|
+
# @param [Hash] param
|
318
|
+
# @return [String] query string
|
319
|
+
# @example
|
320
|
+
def query_string(params)
|
321
|
+
return '' if params.nil?
|
322
|
+
params.keys.collect{|key| "#{URI.escape(key.to_s)}=#{URI.escape(params[key].to_s)}" }.join('&')
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'cgi'
|
3
|
+
|
4
|
+
module MultiForecast
|
5
|
+
module ConversionRule
|
6
|
+
def service_name(path)
|
7
|
+
return path.split('/')[0] if path.count('/') == 2
|
8
|
+
'multiforecast'
|
9
|
+
end
|
10
|
+
|
11
|
+
def section_name(path)
|
12
|
+
return path.split('/')[1] if path.count('/') == 2
|
13
|
+
# + => '%20' is to avoid GF (Kossy?) bug
|
14
|
+
# . => '%2E' because a/./b is recognized as a/b as URL
|
15
|
+
CGI.escape(File.dirname(path)).gsub('+', '%20').gsub('.', '%2E')
|
16
|
+
end
|
17
|
+
|
18
|
+
def graph_name(path)
|
19
|
+
File.basename(path)
|
20
|
+
end
|
21
|
+
|
22
|
+
def path(service_name, section_name, graph_name)
|
23
|
+
return "#{service_name}/#{section_name}/#{graph_name}" unless service_name == "multiforecast"
|
24
|
+
dirname = CGI.unescape(section_name)
|
25
|
+
basename = graph_name
|
26
|
+
dirname == "." ? basename : "#{dirname}/#{basename}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def id(path)
|
30
|
+
@mapping.each do |base_path, base_uri|
|
31
|
+
return base_path if path.index(base_path) == 0
|
32
|
+
end
|
33
|
+
return @mapping.keys.first
|
34
|
+
end
|
35
|
+
|
36
|
+
def ids(path)
|
37
|
+
@mapping.map do |base_path, base_uri|
|
38
|
+
base_path if path.index(base_path) == 0
|
39
|
+
end.compact
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
base_uri = 'http://localhost:5125'
|
4
|
+
|
5
|
+
shared_context "stub_list_graph" do
|
6
|
+
def list_graph_example
|
7
|
+
[
|
8
|
+
{"gfuri"=>'http://localhost:5125',
|
9
|
+
"path"=>"app name/host name/<1sec count",
|
10
|
+
"service_name"=>"multiforecast",
|
11
|
+
"section_name"=>"app%20name%2Fhost%20name",
|
12
|
+
"graph_name"=>"<1sec count",
|
13
|
+
"id"=>1},
|
14
|
+
{"gfuri"=>'http://localhost:5125',
|
15
|
+
"path"=>"app name/host name/<2sec count",
|
16
|
+
"service_name"=>"multiforecast",
|
17
|
+
"section_name"=>"app+name%2Fhost+name",
|
18
|
+
"graph_name"=>"<2sec count",
|
19
|
+
"id"=>2},
|
20
|
+
]
|
21
|
+
end
|
22
|
+
|
23
|
+
proc = Proc.new do
|
24
|
+
# WebMock.allow_net_connect!
|
25
|
+
stub_request(:get, "#{base_uri}/json/list/graph").to_return(:status => 200, :body => list_graph_example.to_json)
|
26
|
+
end
|
27
|
+
before(:each, &proc)
|
28
|
+
end
|
29
|
+
|
30
|
+
shared_context "stub_get_graph" do
|
31
|
+
def graph_example
|
32
|
+
{
|
33
|
+
"gfuri"=>"http://localhost:5125",
|
34
|
+
"path"=>"app name/host name/<1sec count",
|
35
|
+
"number"=>0,
|
36
|
+
"llimit"=>-1000000000,
|
37
|
+
"mode"=>"gauge",
|
38
|
+
"stype"=>"AREA",
|
39
|
+
"adjustval"=>"1",
|
40
|
+
"meta"=>"",
|
41
|
+
"service_name"=>"multiforecast",
|
42
|
+
"gmode"=>"gauge",
|
43
|
+
"color"=>"#cc6633",
|
44
|
+
"created_at"=>"2013/02/02 00:41:11",
|
45
|
+
"section_name"=>"app%20name%2Fhost%20name",
|
46
|
+
"ulimit"=>1000000000,
|
47
|
+
"id"=>1,
|
48
|
+
"graph_name"=>"<1sec count",
|
49
|
+
"description"=>"",
|
50
|
+
"sulimit"=>100000,
|
51
|
+
"unit"=>"",
|
52
|
+
"sort"=>0,
|
53
|
+
"updated_at"=>"2013/02/02 02:32:10",
|
54
|
+
"adjust"=>"*",
|
55
|
+
"type"=>"AREA",
|
56
|
+
"sllimit"=>-100000,
|
57
|
+
"md5"=>"3c59dc048e8850243be8079a5c74d079"
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
proc = Proc.new do
|
62
|
+
stub_request(:get, "#{base_uri}/api/#{gfpath(graph['path'])}").
|
63
|
+
to_return(:status => 200, :body => graph_example.to_json)
|
64
|
+
end
|
65
|
+
before(:each, &proc)
|
66
|
+
end
|
67
|
+
|
68
|
+
shared_context "stub_post_graph" do
|
69
|
+
include_context "stub_get_graph"
|
70
|
+
proc = Proc.new do
|
71
|
+
stub_request(:post, "#{base_uri}/api/#{gfpath(graph['path'])}").
|
72
|
+
to_return(:status => 200, :body => { "error" => 0, "data" => graph_example }.to_json)
|
73
|
+
end
|
74
|
+
before(:each, &proc)
|
75
|
+
end
|
76
|
+
|
77
|
+
shared_context "stub_delete_graph" do
|
78
|
+
proc = Proc.new do
|
79
|
+
stub_request(:post, "#{base_uri}/delete/#{gfpath(graph['path'])}").
|
80
|
+
to_return(:status => 200, :body => { "error" => 0 }.to_json)
|
81
|
+
end
|
82
|
+
before(:each, &proc)
|
83
|
+
end
|
84
|
+
|
85
|
+
shared_context "stub_edit_graph" do
|
86
|
+
include_context "stub_get_graph"
|
87
|
+
|
88
|
+
proc = Proc.new do
|
89
|
+
stub_request(:post, "#{base_uri}/json/edit/graph/#{graph['id']}").
|
90
|
+
to_return(:status => 200, :body => { "error" => 0 }.to_json)
|
91
|
+
end
|
92
|
+
before(:each, &proc)
|
93
|
+
end
|
94
|
+
|
95
|
+
shared_context "stub_list_complex" do
|
96
|
+
def list_complex_example
|
97
|
+
[
|
98
|
+
{"gfuri"=>"http://localhost:5125",
|
99
|
+
"path"=>"app name/host name/complex graph test",
|
100
|
+
"service_name"=>"multiforecast",
|
101
|
+
"section_name"=>"app%20name%2Fhost%20name",
|
102
|
+
"graph_name"=>"<1sec count",
|
103
|
+
"id"=>1},
|
104
|
+
]
|
105
|
+
end
|
106
|
+
|
107
|
+
proc = Proc.new do
|
108
|
+
stub_request(:get, "#{base_uri}/json/list/complex").
|
109
|
+
to_return(:status => 200, :body => list_complex_example.to_json)
|
110
|
+
end
|
111
|
+
before(:each, &proc)
|
112
|
+
end
|
113
|
+
|
114
|
+
shared_context "stub_get_complex" do
|
115
|
+
def complex_example
|
116
|
+
{"gfuri"=>"http://localhost:5125",
|
117
|
+
"path"=>"app name/host name/complex graph test",
|
118
|
+
"service_name"=>"multiforecast",
|
119
|
+
"section_name"=>"app%20name%2Fhost%20name",
|
120
|
+
"graph_name"=>"complex graph test",
|
121
|
+
"number"=>0,
|
122
|
+
"complex"=>true,
|
123
|
+
"created_at"=>"2013/05/20 15:08:28",
|
124
|
+
"id"=>1,
|
125
|
+
"data"=>
|
126
|
+
[{"gmode"=>"gauge", "stack"=>false, "type"=>"AREA", "graph_id"=>218},
|
127
|
+
{"gmode"=>"gauge", "stack"=>true, "type"=>"AREA", "graph_id"=>217}],
|
128
|
+
"sumup"=>false,
|
129
|
+
"description"=>"complex graph test",
|
130
|
+
"sort"=>10,
|
131
|
+
"updated_at"=>"2013/05/20 15:08:28"}
|
132
|
+
end
|
133
|
+
|
134
|
+
proc = Proc.new do
|
135
|
+
stub_request(:get, "#{base_uri}/json/complex/#{gfpath(to_complex['path'])}").
|
136
|
+
to_return(:status => 200, :body => complex_example.to_json)
|
137
|
+
end
|
138
|
+
before(:each, &proc)
|
139
|
+
end
|
140
|
+
|
141
|
+
shared_context "stub_delete_complex" do
|
142
|
+
proc = Proc.new do
|
143
|
+
stub_request(:post, "#{base_uri}/json/delete/complex/#{gfpath(to_complex['path'])}").
|
144
|
+
to_return(:status => 200, :body => { "error" => 0 }.to_json)
|
145
|
+
end
|
146
|
+
before(:each, &proc)
|
147
|
+
end
|
148
|
+
|
149
|
+
shared_context "stub_create_complex" do
|
150
|
+
include_context "stub_list_complex"
|
151
|
+
|
152
|
+
proc = Proc.new do
|
153
|
+
list_graph_example.each do |graph|
|
154
|
+
stub_request(:get, "#{base_uri}/api/#{gfpath(graph['path'])}").
|
155
|
+
to_return(:status => 200, :body => graph.to_json)
|
156
|
+
end
|
157
|
+
|
158
|
+
stub_request(:post, "#{base_uri}/json/create/complex").
|
159
|
+
to_return(:status => 200, :body => { "error" => 0 }.to_json)
|
160
|
+
end
|
161
|
+
before(:each, &proc)
|
162
|
+
end
|
163
|
+
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
shared_context "let_graph" do
|
3
|
+
include_context "stub_list_graph" if ENV['MOCK'] == 'on'
|
4
|
+
let(:graphs) { multiforecast.list_graph }
|
5
|
+
let(:graph) { graphs.first }
|
6
|
+
end
|
7
|
+
|
8
|
+
shared_context "setup_graph" do
|
9
|
+
include_context "let_graph"
|
10
|
+
include_context "stub_post_graph" if ENV['MOCK'] == 'on'
|
11
|
+
include_context "stub_delete_graph" if ENV['MOCK'] == 'on'
|
12
|
+
before(:all) {
|
13
|
+
multiforecast.delete_graph("app name/host name/<1sec count") rescue nil
|
14
|
+
multiforecast.delete_graph("app name/host name/<2sec count") rescue nil
|
15
|
+
multiforecast.post_graph("app name/host name/<1sec count", { 'number' => 0 }) rescue nil
|
16
|
+
multiforecast.post_graph("app name/host name/<2sec count", { 'number' => 0 }) rescue nil
|
17
|
+
}
|
18
|
+
after(:all) {
|
19
|
+
multiforecast.delete_graph("app name/host name/<1sec count") rescue nil
|
20
|
+
multiforecast.delete_graph("app name/host name/<2sec count") rescue nil
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
shared_context "let_complex" do
|
25
|
+
include_context "setup_graph"
|
26
|
+
let(:from_graphs) do
|
27
|
+
[
|
28
|
+
{
|
29
|
+
"path" => graphs[0]["path"],
|
30
|
+
"gmode" => "gauge",
|
31
|
+
"stack" => false,
|
32
|
+
"type" => "AREA",
|
33
|
+
},
|
34
|
+
{
|
35
|
+
"path" => graphs[1]["path"],
|
36
|
+
"gmode" => "gauge",
|
37
|
+
"stack" => false,
|
38
|
+
"type" => "AREA"
|
39
|
+
},
|
40
|
+
]
|
41
|
+
end
|
42
|
+
let(:to_complex) do
|
43
|
+
{
|
44
|
+
"path" => "app name/host name/complex graph test",
|
45
|
+
"description" => "complex graph test",
|
46
|
+
"sort" => 10
|
47
|
+
}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
shared_context "setup_complex" do
|
52
|
+
include_context "let_complex"
|
53
|
+
include_context "stub_create_complex" if ENV['MOCK'] == 'on'
|
54
|
+
include_context "stub_delete_complex" if ENV['MOCK'] == 'on'
|
55
|
+
before do
|
56
|
+
multiforecast.delete_complex(to_complex["path"]) rescue nil
|
57
|
+
multiforecast.create_complex(from_graphs, to_complex) rescue nil
|
58
|
+
end
|
59
|
+
after do
|
60
|
+
multiforecast.delete_complex(to_complex["path"]) rescue nil
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'multiforecast-client'
|
2
|
+
require 'multiforecast/shared_context/setup.rb'
|
3
|
+
require 'multiforecast/shared_context/mock.rb'
|
4
|
+
|
5
|
+
include MultiForecast::ConversionRule
|
6
|
+
|
7
|
+
def e(str)
|
8
|
+
CGI.escape(str).gsub('+', '%20') if str
|
9
|
+
end
|
10
|
+
|
11
|
+
def gfpath(path)
|
12
|
+
"#{e service_name(path)}/#{e section_name(path)}/#{e graph_name(path)}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def base_uri
|
16
|
+
'http://localhost:5125'
|
17
|
+
end
|
18
|
+
|
19
|
+
def multiforecast(opts = {})
|
20
|
+
opts['mapping'] ||= { '' => base_uri }
|
21
|
+
MultiForecast::Client.new(opts)
|
22
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
shared_examples_for 'graph_uri_long_metrics' do
|
2
|
+
it { subject.should match(/(\?|&)t=c(&|$)/) }
|
3
|
+
it_should_behave_like 'graph_uri_params_fromto'
|
4
|
+
it_should_behave_like 'graph_uri_params_widthheight'
|
5
|
+
end
|
6
|
+
|
7
|
+
shared_examples_for 'graph_uri_short_metrics' do
|
8
|
+
it { subject.should match(/(\?|&)t=sc(&|$)/) }
|
9
|
+
it_should_behave_like 'graph_uri_params_fromto'
|
10
|
+
it_should_behave_like 'graph_uri_params_widthheight'
|
11
|
+
end
|
12
|
+
|
13
|
+
shared_examples_for 'graph_uri_params' do
|
14
|
+
it_should_behave_like 'graph_uri_params_term'
|
15
|
+
it_should_behave_like 'graph_uri_params_widthheight'
|
16
|
+
end
|
17
|
+
|
18
|
+
shared_examples_for 'graph_uri_params_term' do
|
19
|
+
it { subject.should match(/(\?|&)t=#{params['t']}(&|$)/) }
|
20
|
+
end
|
21
|
+
|
22
|
+
shared_examples_for 'graph_uri_params_widthheight' do
|
23
|
+
it { subject.should match(/(\?|&)width=#{params['width'].to_s}(&|$)/) }
|
24
|
+
it { subject.should match(/(\?|&)height=#{params['height'].to_s}(&|$)/) }
|
25
|
+
end
|
26
|
+
|
27
|
+
shared_examples_for 'graph_uri_params_fromto' do
|
28
|
+
it { subject.should match(/(\?|&)from=#{Regexp.escape(URI.escape(params['from'].to_s))}(&|$)/) }
|
29
|
+
it { subject.should match(/(\?|&)to=#{Regexp.escape(URI.escape(params['to'].to_s))}(&|$)/) }
|
30
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#! /usr/bin/env gem build
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.name = 'multiforecast-client'
|
6
|
+
gem.version = File.read(File.expand_path('VERSION', File.dirname(__FILE__))).chomp
|
7
|
+
gem.authors = ["Naotoshi Seo"]
|
8
|
+
gem.email = ["sonots@gmail.com"]
|
9
|
+
gem.homepage = "https://github.com/sonots/multiforecast-client"
|
10
|
+
gem.summary = "Multiple GrowthForecast Client"
|
11
|
+
gem.description = gem.summary
|
12
|
+
|
13
|
+
gem.files = `git ls-files`.split($\)
|
14
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
15
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
16
|
+
gem.require_paths = ["lib"]
|
17
|
+
|
18
|
+
gem.add_runtime_dependency "growthforecast-client", "~> 0.62.0"
|
19
|
+
|
20
|
+
# for testing
|
21
|
+
gem.add_development_dependency "rake"
|
22
|
+
gem.add_development_dependency "rspec", "~> 2.11"
|
23
|
+
gem.add_development_dependency "webmock"
|
24
|
+
|
25
|
+
# for debug
|
26
|
+
gem.add_development_dependency "pry"
|
27
|
+
gem.add_development_dependency "pry-nav"
|
28
|
+
gem.add_development_dependency "tapp"
|
29
|
+
end
|