multiforecast-client 0.62.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|