multiforecast-client 0.62.0.5 → 0.62.0.8

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1da64cc1b3b63ef49933d1f4ebf864a3cb4af295
4
- data.tar.gz: 7aaf9779934ec727c02a61696b37b2004d332554
3
+ metadata.gz: 0b9877d259aaf94156087bbac8bb9aa66694db1c
4
+ data.tar.gz: 187c70b320517e6df23d68877d9bd881b5f55c08
5
5
  SHA512:
6
- metadata.gz: d7faed48ecf007ddc67ea66dbf3919a35382ac3663d9fdf2e1f5a3443e068f1b1c8ffe4a16f05ef3abc7e4552c29703cfd18ba0c0d0a5ef71307182b8a6a36ed
7
- data.tar.gz: f3d947e1a93f8901e2edcc01dd650aa386b9f8704916a443afa39e5160bff0783d8d685a510732a5ffa2cfa9fe2d87a992359e18453831db6738e37f4fd9fd17
6
+ metadata.gz: ddc753bd90d278f704fc8aa87ee2e04fe4ce8e1b9aed7d11a21ed94912f17a47df6c98a7999d314261015d84d02f11cda1977341e760d866d8920c8e3ee036ca
7
+ data.tar.gz: 0cce09b837eb7a5cd5a1a04dbca59fe69a7cecd58da04d45129e202bca6c5d3cd0f3644fc4e1e315ceb84280697da59013b40f76b1c2b152200f1ec76d9b9e09
data/.gitignore CHANGED
@@ -12,3 +12,4 @@ tags
12
12
  tmp/*
13
13
  .yardoc
14
14
  multiforecast.yml
15
+ pkg
data/CHANGELOG.md CHANGED
@@ -1,3 +1,27 @@
1
+ # 0.62.0.8 (2013/09/20)
2
+
3
+ Enhancements:
4
+
5
+ * Add `--graph-names` options to `delete` sub command
6
+
7
+ # 0.62.0.7 (2013/09/19)
8
+
9
+ Enhancements:
10
+
11
+ * Add `color` and `create_complex` sub command
12
+
13
+ # 0.62.0.6 (2013/09/18)
14
+
15
+ CHANGES
16
+
17
+ * Change the github repositry from sonots to yohoushi
18
+
19
+ # 0.62.0.5 (2013/07/27)
20
+
21
+ Fix
22
+
23
+ * Fix the `lstrip`
24
+
1
25
  # 0.62.0.4 (2013/07/25)
2
26
 
3
27
  CHANGES
@@ -8,7 +32,7 @@ CHANGES
8
32
 
9
33
  Fix
10
34
 
11
- * Escape a white space to %20, not + to avoid GF (Kossy?) bug
35
+ * Escape a white space to %20, not +.
12
36
 
13
37
  # 0.62.0.2 (2013/07/07)
14
38
 
data/README.md CHANGED
@@ -59,12 +59,24 @@ Post a number and create a graph:
59
59
  $ multiforecast post '{"number":0}' 'foo/a/b/c' -c multiforecast.yml
60
60
  ```
61
61
 
62
- Delete a graph or graphs under a path:
62
+ Delete a graph or graphs under a base path:
63
63
 
64
64
  ```
65
65
  $ multiforecast delete 'foo/' -c multiforecast.yml
66
66
  ```
67
67
 
68
+ Change the color of graphs under a base path:
69
+
70
+ ```
71
+ $ multiforecast color -k '2xx_count:#1111cc' '3xx_count:#11cc11' -b 'foo/' -c multiforecast.yml
72
+ ```
73
+
74
+ Create complex graphs under a base path:
75
+
76
+ ```
77
+ $ multiforecast create_complex -f 2xx_count 3xx_count -t status_count -b 'foo/' -c multiforecast.yml
78
+ ```
79
+
68
80
  See help for more:
69
81
 
70
82
  ```
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.62.0.5
1
+ 0.62.0.8
@@ -4,12 +4,14 @@ require 'yaml'
4
4
  require 'multiforecast-client'
5
5
 
6
6
  class MultiForecast::CLI < Thor
7
+ include ::MultiForecast::ConversionRule
7
8
  class_option :config, :aliases => ["-c"], :type => :string
8
9
  class_option :silent, :aliases => ["-S"], :type => :boolean
9
10
 
10
11
  def initialize(args = [], opts = [], config = {})
11
12
  super(args, opts, config)
12
13
 
14
+ # NOTE: Please note that @options receives only strings, no symbols anymore
13
15
  if options['config'] && File.exists?(options['config'])
14
16
  @options = YAML.load_file(options['config']).merge(@options)
15
17
  end
@@ -31,39 +33,114 @@ class MultiForecast::CLI < Thor
31
33
  long_desc <<-LONGDESC
32
34
  Post a parameter to a path
33
35
 
34
- ex)
35
- $ multiforecast post '{"number":0}' 'test/test' -c multiforecast.yml
36
+ ex) multiforecast post '{"number":0}' 'test/test' -c multiforecast.yml
36
37
  LONGDESC
37
38
  def post(json, path)
38
- begin
39
+ path = lstrip(path, '/')
40
+ exec do
39
41
  res = @client.post_graph(path, JSON.parse(json))
40
42
  $stdout.puts res unless @options['silent']
41
- rescue => e
42
- $stderr.puts "\tclass:#{e.class}\t#{e.message}"
43
43
  end
44
44
  end
45
45
 
46
+ # NOTE: base_path argument should be a requirement for foolproof
46
47
  desc 'delete <base_path>', 'Delete a graph or graphs under a path'
48
+ long_desc <<-LONGDESC
49
+ Delete a graph or graphs under a path
50
+
51
+ ex) multiforecast delete 'test/test' -c multiforecast.yml
52
+ LONGDESC
53
+ option :graph_names, :type => :array, :aliases => '-g'
47
54
  def delete(base_path)
55
+ base_path = lstrip(base_path, '/')
56
+
57
+ graphs = @client.list_graph(base_path)
58
+ delete_graphs(graphs, @options['graph_names'])
59
+
60
+ complexes = @client.list_complex(base_path)
61
+ delete_complexes(complexes, @options['graph_names'])
62
+ $stderr.puts "Not found" if graphs.empty? and complexes.empty? unless @options['silent']
63
+ end
64
+
65
+ desc 'color', 'change the color of graphs'
66
+ long_desc <<-LONGDESC
67
+ Change the color of graphs
68
+
69
+ ex) multiforecast color -k '2xx_count:#1111cc' '3xx_count:#11cc11' -c multiforecast.yml
70
+ LONGDESC
71
+ option :colors, :type => :hash, :aliases => '-k', :required => true, :banner => 'GRAPH_NAME:COLOR ...'
72
+ option :base_path, :type => :string, :aliases => '-b'
73
+ def color
74
+ base_path = lstrip(@options['base_path'], '/') if @options['base_path']
75
+ graphs = @client.list_graph(base_path)
76
+ setup_colors(@options['colors'], graphs)
77
+ end
78
+
79
+ desc 'create_complex', 'create complex graphs'
80
+ long_desc <<-LONGDESC
81
+ Create complex graphs under a url
82
+
83
+ ex) multiforecast create_complex -f 2xx_count 3xx_count -t status_count -c multiforecast.yml
84
+ LONGDESC
85
+ option :from_graphs, :type => :array, :aliases => '-f', :required => true, :banner => 'GRAPH_NAMES ...'
86
+ option :to_complex, :type => :string, :aliases => '-t', :required => true
87
+ option :base_path, :type => :string, :aliases => '-b'
88
+ def create_complex
89
+ base_path = lstrip(@options['base_path'], '/') if @options['base_path']
48
90
  graphs = @client.list_graph(base_path)
91
+ setup_complex(options['from_graphs'], options['to_complex'], graphs)
92
+ end
93
+
94
+ private
95
+
96
+ def delete_graphs(graphs, graph_names = nil)
49
97
  graphs.each do |graph|
50
- begin
51
- @client.delete_graph(graph['path'])
52
- $stdout.puts "Deleted #{graph['path']}" unless @options['silent']
53
- rescue => e
54
- $stderr.puts "\tclass:#{e.class}\t#{e.message}"
55
- end
98
+ path = graph['path']
99
+ next if graph_names and !graph_names.include?(File.basename(path))
100
+ puts "Delete #{path}" unless @options['silent']
101
+ exec { @client.delete_graph(path) }
56
102
  end
57
- complexes = @client.list_complex(base_path)
103
+ end
104
+
105
+ def delete_complexes(complexes, graph_names = nil)
58
106
  complexes.each do |graph|
59
- begin
60
- @client.delete_complex(graph['path'])
61
- $stdout.puts "Deleted #{graph['path']}" unless @options['silent']
62
- rescue => e
63
- puts "\tclass:#{e.class}\t#{e.message}"
64
- end
107
+ path = graph['path']
108
+ next if graph_names and !graph_names.include?(File.basename(path))
109
+ puts "Delete #{path}" unless @options['silent']
110
+ exec { @client.delete_complex(path) }
111
+ end
112
+ end
113
+
114
+ def setup_colors(colors, graphs)
115
+ graphs.each do |graph|
116
+ path = graph['path']
117
+ next unless color = colors[File.basename(path)]
118
+ data = { 'color' => color }
119
+ puts "Setup #{path} with #{color}" unless @options['silent']
120
+ exec { @client.edit_graph(path, data) }
121
+ end
122
+ end
123
+
124
+ def setup_complex(from_graphs, to_complex, graphs)
125
+ from_graph_first = from_graphs.first
126
+ graphs.each do |graph|
127
+ next unless File.basename(graph['path']) == from_graph_first
128
+ dirname = File.dirname(graph['path'])
129
+
130
+ base = {'gmode' => 'gauge', 'stack' => true, 'type' => 'AREA'}
131
+ from_graphs_params = from_graphs.map {|name| base.merge('path' => "#{dirname}/#{name}") }
132
+ to_complex_params = { 'path' => "#{dirname}/#{to_complex}", 'sort' => 0 }
133
+ puts "Setup #{dirname}/#{to_complex} with #{from_graphs}" unless @options['silent']
134
+ exec { @client.create_complex(from_graphs_params, to_complex_params) }
135
+ end
136
+ end
137
+
138
+ def exec(&blk)
139
+ begin
140
+ yield
141
+ rescue => e
142
+ $stderr.puts "\tclass:#{e.class}\t#{e.message}"
65
143
  end
66
- $stderr.puts "Not found" if graphs.empty? and complexes.empty? unless @options['silent']
67
144
  end
68
145
  end
69
146
 
@@ -15,7 +15,10 @@ module MultiForecast
15
15
  # @param [Hash] opts
16
16
  # [Hash] mapping: Mapping rules from `path` to GrowthForecast's `base_uri`.
17
17
  def initialize(opts = {})
18
- @mapping = opts['mapping'] || { '' => 'http://localhost:5125' }
18
+ @mapping = {}
19
+ mapping = opts['mapping'] || { '' => 'http://localhost:5125' }
20
+ # remove heading / of path
21
+ mapping.each {|key, val| @mapping[lstrip(key, '/')] = val }
19
22
  @short_metrics = opts['short_metrics'] || true
20
23
 
21
24
  @clients = {}
@@ -77,6 +80,7 @@ module MultiForecast
77
80
  end
78
81
 
79
82
  # Get the list of graphs, /json/list/graph
83
+ # @param [String] base_path
80
84
  # @return [Hash] list of graphs
81
85
  # @example
82
86
  # [
@@ -273,9 +277,10 @@ module MultiForecast
273
277
  # height [String] the height of image to show
274
278
  # @return [Hash] error response
275
279
  # @example
276
- def get_graph_uri(path, params = {})
277
- params = preprocess_time_params(params) if params
278
- "#{@base_uris[id(path)]}/graph/#{uri_escape(service_name(path))}/#{uri_escape(section_name(path))}/#{uri_escape(graph_name(path))}?#{query_string(params)}"
280
+ def get_graph_uri(path, params = nil)
281
+ params ||= {}
282
+ params = preprocess_time_params(params) unless params.empty?
283
+ "#{@base_uris[id(path)]}/graph/#{uri_escape(service_name(path))}/#{uri_escape(section_name(path))}/#{uri_escape(graph_name(path))}#{'?' unless params.empty?}#{query_string(params)}"
279
284
  end
280
285
 
281
286
  # Get complex graph image uri
@@ -291,9 +296,10 @@ module MultiForecast
291
296
  # height [String] the height of image to show
292
297
  # @return [Hash] error response
293
298
  # @example
294
- def get_complex_uri(path, params = {})
295
- params = preprocess_time_params(params) if params
296
- "#{@base_uris[id(path)]}/complex/graph/#{uri_escape(service_name(path))}/#{uri_escape(section_name(path))}/#{uri_escape(graph_name(path))}?#{query_string(params)}"
299
+ def get_complex_uri(path, params = nil)
300
+ params ||= {}
301
+ params = preprocess_time_params(params) unless params.empty?
302
+ "#{@base_uris[id(path)]}/complex/graph/#{uri_escape(service_name(path))}/#{uri_escape(section_name(path))}/#{uri_escape(graph_name(path))}#{'?' unless params.empty?}#{query_string(params)}"
297
303
  end
298
304
 
299
305
  # process the time params (from and to)
@@ -4,8 +4,10 @@ require 'cgi'
4
4
  module MultiForecast
5
5
  module ConversionRule
6
6
  def uri_escape(string)
7
- # + => '%20' is to avoid GF (Kossy?) bug
8
- # . => '%2E' because a/./b is recognized as a/b as URL
7
+ # Here, we want to do CGI.escape to encode '/', but GF (Plack?) does URI.unescape when it receives.
8
+ # Major Difference: In URI.escape, ' ' => '%20'. In CGI.escape, ' ' => '+'.
9
+ # Thus, we need to convert as ' ' => '+', and then, '+' => '%20' so that GF can unescape '%20' => ' '.
10
+ # '.' => '%2E' because a/./b is recognized as a/b as URL
9
11
  CGI.escape(string).gsub('+', '%20').gsub('.', '%2E') if string
10
12
  end
11
13
 
@@ -28,3 +28,7 @@ shared_examples_for 'graph_uri_params_fromto' do
28
28
  it { subject.should match(/(\?|&)from=#{Regexp.escape(URI.escape(params['from'].to_s))}(&|$)/) }
29
29
  it { subject.should match(/(\?|&)to=#{Regexp.escape(URI.escape(params['to'].to_s))}(&|$)/) }
30
30
  end
31
+
32
+ shared_examples_for 'graph_uri_empty_params' do
33
+ it { subject.should_not match(/(\?|&)/) }
34
+ end
@@ -6,9 +6,10 @@ Gem::Specification.new do |gem|
6
6
  gem.version = File.read(File.expand_path('VERSION', File.dirname(__FILE__))).chomp
7
7
  gem.authors = ["Naotoshi Seo"]
8
8
  gem.email = ["sonots@gmail.com"]
9
- gem.homepage = "https://github.com/sonots/multiforecast-client"
9
+ gem.homepage = "https://github.com/yohoushi/multiforecast-client"
10
10
  gem.summary = "Multiple GrowthForecast Client"
11
11
  gem.description = gem.summary
12
+ gem.licenses = ['MIT']
12
13
 
13
14
  gem.files = `git ls-files`.split($\)
14
15
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
@@ -8,6 +8,18 @@ describe MultiForecast::Client do
8
8
  complex_keys = %w[number complex created_at service_name section_name id graph_name data sumup
9
9
  description sort updated_at]
10
10
 
11
+ context "#initialize" do
12
+ context "typical" do
13
+ subject { MultiForecast::Client.new('mapping' => {'app1/' => 'http://localhost:5125'}) }
14
+ it { subject.instance_variable_get(:@mapping).keys.first.should == 'app1/' }
15
+ end
16
+
17
+ context "leading / of mapping path should be stripped" do
18
+ subject { MultiForecast::Client.new('mapping' => {'/app1/' => 'http://localhost:5125'}) }
19
+ it { subject.instance_variable_get(:@mapping).keys.first.should == 'app1/' }
20
+ end
21
+ end
22
+
11
23
  context "#list_graph" do
12
24
  include_context "stub_list_graph" if ENV['MOCK'] == 'on'
13
25
  subject { graphs }
@@ -86,7 +98,7 @@ describe MultiForecast::Client do
86
98
  end
87
99
  end
88
100
 
89
- describe "graph_uri" do
101
+ describe "graph_uri_term" do
90
102
  let(:params) do
91
103
  {
92
104
  't' => 'h',
@@ -104,6 +116,30 @@ describe MultiForecast::Client do
104
116
  end
105
117
  end
106
118
 
119
+ describe "graph_uri_empty" do
120
+ let(:params) { {} }
121
+ context "#get_graph_uri" do
122
+ subject { multiforecast.get_graph_uri(graph["path"], params) }
123
+ it_should_behave_like 'graph_uri_empty_params'
124
+ end
125
+ context "#get_complex_uri" do
126
+ subject { multiforecast.get_complex_uri(graph["path"], params) }
127
+ it_should_behave_like 'graph_uri_empty_params'
128
+ end
129
+ end
130
+
131
+ describe "graph_uri_nil" do
132
+ let(:params) { nil }
133
+ context "#get_graph_uri" do
134
+ subject { multiforecast.get_graph_uri(graph["path"], params) }
135
+ it_should_behave_like 'graph_uri_empty_params'
136
+ end
137
+ context "#get_complex_uri" do
138
+ subject { multiforecast.get_complex_uri(graph["path"], params) }
139
+ it_should_behave_like 'graph_uri_empty_params'
140
+ end
141
+ end
142
+
107
143
  describe "graph_uri_fromto" do
108
144
  shared_context "short_period" do
109
145
  before { now = Time.now; Time.stub(:now) { now } }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: multiforecast-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.62.0.5
4
+ version: 0.62.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Naotoshi Seo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-07-26 00:00:00.000000000 Z
11
+ date: 2013-09-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: growthforecast-client
@@ -141,8 +141,9 @@ files:
141
141
  - spec/multiforecast/client_spec.rb
142
142
  - spec/multiforecast/conversion_rule_spec.rb
143
143
  - spec/spec_helper.rb
144
- homepage: https://github.com/sonots/multiforecast-client
145
- licenses: []
144
+ homepage: https://github.com/yohoushi/multiforecast-client
145
+ licenses:
146
+ - MIT
146
147
  metadata: {}
147
148
  post_install_message:
148
149
  rdoc_options: []
@@ -168,4 +169,3 @@ test_files:
168
169
  - spec/multiforecast/client_spec.rb
169
170
  - spec/multiforecast/conversion_rule_spec.rb
170
171
  - spec/spec_helper.rb
171
- has_rdoc: