dogapi 1.22.0 → 1.23.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rspec +1 -1
  4. data/.rubocop.yml +13 -0
  5. data/.travis.yml +3 -1
  6. data/CHANGELOG.md +16 -1
  7. data/Gemfile +6 -3
  8. data/README.rdoc +1 -1
  9. data/Rakefile +8 -15
  10. data/lib/capistrano/datadog.rb +12 -12
  11. data/lib/capistrano/datadog/v2.rb +5 -5
  12. data/lib/capistrano/datadog/v3.rb +9 -4
  13. data/lib/dogapi/common.rb +43 -39
  14. data/lib/dogapi/event.rb +4 -4
  15. data/lib/dogapi/facade.rb +57 -54
  16. data/lib/dogapi/metric.rb +2 -2
  17. data/lib/dogapi/v1/alert.rb +17 -80
  18. data/lib/dogapi/v1/comment.rb +8 -39
  19. data/lib/dogapi/v1/dash.rb +20 -67
  20. data/lib/dogapi/v1/embed.rb +11 -57
  21. data/lib/dogapi/v1/event.rb +37 -72
  22. data/lib/dogapi/v1/metric.rb +17 -46
  23. data/lib/dogapi/v1/monitor.rb +54 -194
  24. data/lib/dogapi/v1/screenboard.rb +8 -77
  25. data/lib/dogapi/v1/search.rb +5 -11
  26. data/lib/dogapi/v1/service_check.rb +6 -15
  27. data/lib/dogapi/v1/snapshot.rb +10 -15
  28. data/lib/dogapi/v1/tag.rb +39 -99
  29. data/lib/dogapi/v1/user.rb +15 -69
  30. data/lib/dogapi/version.rb +1 -1
  31. data/spec/integration/alert_spec.rb +48 -0
  32. data/spec/integration/comment_spec.rb +32 -0
  33. data/spec/integration/common_spec.rb +32 -0
  34. data/spec/integration/dash_spec.rb +60 -0
  35. data/spec/integration/embed_spec.rb +43 -0
  36. data/spec/integration/event_spec.rb +73 -0
  37. data/spec/integration/metric_spec.rb +96 -0
  38. data/spec/integration/monitor_spec.rb +106 -0
  39. data/spec/integration/screenboard_spec.rb +61 -0
  40. data/spec/integration/search_spec.rb +11 -0
  41. data/spec/integration/service_check_spec.rb +12 -0
  42. data/spec/integration/snapshot_spec.rb +24 -0
  43. data/spec/integration/tag_spec.rb +66 -0
  44. data/spec/integration/user_spec.rb +46 -0
  45. data/spec/spec_helper.rb +85 -16
  46. data/spec/unit/common_spec.rb +101 -0
  47. data/spec/unit/facade_spec.rb +79 -0
  48. metadata +55 -52
  49. data/spec/alerts_spec.rb +0 -33
  50. data/spec/common_spec.rb +0 -37
  51. data/spec/facade_spec.rb +0 -166
  52. data/spec/support/cassettes/Alerts/create/returns_HTTP_code_200.yml +0 -114
  53. data/spec/support/cassettes/Alerts/create/returns_a_valid_event_ID.yml +0 -114
  54. data/spec/support/cassettes/Alerts/create/returns_the_same_query_as_sent.yml +0 -114
  55. data/spec/support/cassettes/Facade/Events/emits_aggregate_events.yml +0 -193
  56. data/spec/support/cassettes/Facade/Events/emits_events_and_retrieves_them.yml +0 -100
  57. data/spec/support/cassettes/Facade/Events/emits_events_with_specified_priority.yml +0 -98
  58. data/spec/support/cassettes/Facade/Tags/adds_updates_and_detaches_tags.yml +0 -442
  59. data/tests/test_alerts.rb +0 -38
  60. data/tests/test_base.rb +0 -30
  61. data/tests/test_client.rb +0 -23
  62. data/tests/test_comments.rb +0 -39
  63. data/tests/test_dashes.rb +0 -85
  64. data/tests/test_embed.rb +0 -194
  65. data/tests/test_monitors.rb +0 -192
  66. data/tests/test_screenboard.rb +0 -90
  67. data/tests/test_search.rb +0 -20
  68. data/tests/test_snapshot.rb +0 -28
  69. data/tests/test_users.rb +0 -65
@@ -0,0 +1,12 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe Dogapi::Client do
4
+ SC_BODY = { check: 'app.is_ok', host_name: 'app1', status: 0 }.freeze
5
+ SC_ARGS = SC_BODY.values
6
+
7
+ describe '#service_check' do
8
+ it_behaves_like 'an api method with options',
9
+ :service_check, SC_ARGS,
10
+ :post, '/check_run', SC_BODY
11
+ end
12
+ end
@@ -0,0 +1,24 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe Dogapi::Client do
4
+ SNAP_QUERY = 'system.load.1{*}'.freeze
5
+ START_TS = Time.now.to_i
6
+ END_TS = START_TS + 10
7
+ EVENT_QUERY = 'hotfixes'.freeze
8
+
9
+ PARAMS = { metric_query: SNAP_QUERY, start: START_TS, end: END_TS }.freeze
10
+ EXTENDED_PARAMS = PARAMS.merge(event_query: EVENT_QUERY)
11
+
12
+ describe '#snapshot' do
13
+ context 'when creating a simple graph' do
14
+ it_behaves_like 'an api method with params',
15
+ :graph_snapshot, [],
16
+ :get, '/graph/snapshot', PARAMS
17
+ end
18
+ context 'when adding an event overlay' do
19
+ it_behaves_like 'an api method with params',
20
+ :graph_snapshot, [],
21
+ :get, '/graph/snapshot', EXTENDED_PARAMS
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,66 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe Dogapi::Client do
4
+ HOST_NAME = 'test.example.com'.freeze
5
+ SOURCE = 'chef_custom'.freeze
6
+ TAGS = ['role:web', 'environment:test'].freeze
7
+
8
+ describe '#add_tags' do
9
+ context 'whithout precising the source' do
10
+ it_behaves_like 'an api method',
11
+ :add_tags, [HOST_NAME, TAGS],
12
+ :post, "/tags/hosts/#{HOST_NAME}", tags: TAGS
13
+ end
14
+ context 'while precising the source' do
15
+ it_behaves_like 'an api method with params',
16
+ :add_tags, [HOST_NAME, TAGS],
17
+ :post, "/tags/hosts/#{HOST_NAME}", source: SOURCE
18
+ end
19
+ end
20
+
21
+ describe '#update_tags' do
22
+ context 'whithout precising the source' do
23
+ it_behaves_like 'an api method',
24
+ :update_tags, [HOST_NAME, TAGS],
25
+ :put, "/tags/hosts/#{HOST_NAME}", tags: TAGS
26
+ end
27
+ context 'while precising the source' do
28
+ it_behaves_like 'an api method with params',
29
+ :update_tags, [HOST_NAME, TAGS],
30
+ :put, "/tags/hosts/#{HOST_NAME}", source: SOURCE
31
+ end
32
+ end
33
+
34
+ describe '#host_tags' do
35
+ context 'whithout precising the source' do
36
+ it_behaves_like 'an api method',
37
+ :host_tags, [HOST_NAME],
38
+ :get, "/tags/hosts/#{HOST_NAME}"
39
+ end
40
+ context 'while precising the source' do
41
+ it_behaves_like 'an api method with params',
42
+ :host_tags, [HOST_NAME],
43
+ :get, "/tags/hosts/#{HOST_NAME}", source: SOURCE, by_source: true
44
+ end
45
+ end
46
+
47
+ describe '#detach_tags' do
48
+ context 'whithout precising the source' do
49
+ it_behaves_like 'an api method',
50
+ :detach_tags, [HOST_NAME],
51
+ :delete, "/tags/hosts/#{HOST_NAME}"
52
+ end
53
+
54
+ context 'while precising the source' do
55
+ it_behaves_like 'an api method with params',
56
+ :detach_tags, [HOST_NAME],
57
+ :delete, "/tags/hosts/#{HOST_NAME}", source: SOURCE
58
+ end
59
+ end
60
+
61
+ describe '#all_tags' do
62
+ it_behaves_like 'an api method',
63
+ :all_tags, [],
64
+ :get, '/tags/hosts'
65
+ end
66
+ end
@@ -0,0 +1,46 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe Dogapi::Client do
4
+ USER_HANDLE = 'test@example.com'.freeze
5
+ USER_DESCRIPTION = {
6
+ handle: USER_HANDLE,
7
+ name: 'TEST'
8
+ }.freeze
9
+ USER_EMAILS = (1..4).map { |i| "test#{i}@example.com" }
10
+
11
+ describe '#invite' do
12
+ it_behaves_like 'an api method with options',
13
+ :invite, [USER_EMAILS],
14
+ :post, '/invite_users', 'emails' => USER_EMAILS
15
+ end
16
+
17
+ describe '#create_user' do
18
+ it_behaves_like 'an api method',
19
+ :create_user, [USER_DESCRIPTION],
20
+ :post, '/user', USER_DESCRIPTION
21
+ end
22
+
23
+ describe '#get_user' do
24
+ it_behaves_like 'an api method',
25
+ :get_user, [USER_HANDLE],
26
+ :get, "/user/#{USER_HANDLE}"
27
+ end
28
+
29
+ describe '#get_all_users' do
30
+ it_behaves_like 'an api method',
31
+ :get_all_users, [],
32
+ :get, '/user'
33
+ end
34
+
35
+ describe '#update_user' do
36
+ it_behaves_like 'an api method',
37
+ :update_user, [USER_HANDLE, USER_DESCRIPTION],
38
+ :put, "/user/#{USER_HANDLE}", USER_DESCRIPTION
39
+ end
40
+
41
+ describe '#disable_user' do
42
+ it_behaves_like 'an api method',
43
+ :disable_user, [USER_HANDLE],
44
+ :delete, "/user/#{USER_HANDLE}"
45
+ end
46
+ end
@@ -1,30 +1,99 @@
1
1
  require 'rspec'
2
- require 'vcr'
2
+ require 'simplecov'
3
+ require 'webmock/rspec'
4
+
5
+ SimpleCov.start do
6
+ add_filter 'spec'
7
+ end
8
+
9
+ WebMock.disable_net_connect!(allow_localhost: false)
3
10
 
4
11
  # include our code and methods
5
12
  require 'dogapi'
6
13
 
7
- # Load any custom matchers
8
- Dir[File.join(File.dirname(__FILE__), "/support/**/*.rb")].each { |f| require f }
14
+ DATADOG_HOST = 'app.datadoghq.com'.freeze
15
+ ENV['DATADOG_HOST'] = 'http://' + DATADOG_HOST
16
+
17
+ module SpecDog
18
+ extend RSpec::SharedContext
19
+
20
+ let(:api_key) { 'API_KEY' }
21
+ let(:app_key) { 'APP_KEY' }
22
+ let(:dog) { Dogapi::Client.new(api_key, app_key, 'data.dog', nil, false) }
23
+ let(:api_url) { "#{DATADOG_HOST}/api/v1" }
24
+ let(:default_query) { { api_key: api_key, application_key: app_key } }
25
+
26
+ shared_examples 'an api method' do |command, args, request, endpoint, body|
27
+ it 'queries the api' do
28
+ url = api_url + endpoint
29
+ stub_request(request, /#{url}/).to_return(body: '{}').then.to_raise(StandardError)
30
+ expect(dog.send(command, *args)).to eq ['200', {}]
31
+
32
+ body = MultiJson.dump(body) if body
33
+
34
+ expect(WebMock).to have_requested(request, url).with(
35
+ query: default_query,
36
+ body: body
37
+ )
38
+ end
39
+ end
40
+
41
+ shared_examples 'an api method with options' do |command, args, request, endpoint, body|
42
+ include_examples 'an api method', command, args, request, endpoint, body
43
+ it 'queries the api with options' do
44
+ url = api_url + endpoint
45
+ options = { 'zzz' => 'aaa' }
46
+ stub_request(request, /#{url}/).to_return(body: '{}').then.to_raise(StandardError)
47
+ expect(dog.send(command, *args, options)).to eq ['200', {}]
48
+
49
+ body = MultiJson.dump(body ? (body.merge options) : options)
50
+
51
+ expect(WebMock).to have_requested(request, url).with(
52
+ query: default_query,
53
+ body: body
54
+ )
55
+ end
56
+ end
57
+
58
+ shared_examples 'an api method with params' do |command, args, request, endpoint, params|
59
+ it 'queries the api with params' do
60
+ url = api_url + endpoint
61
+ stub_request(request, /#{url}/).to_return(body: '{}').then.to_raise(StandardError)
62
+ expect(dog.send(command, *args, *params.values)).to eq ['200', {}]
63
+
64
+ params.each { |k, v| params[k] = v.join(',') if v.is_a? Array }
65
+ params = params.merge default_query
66
+
67
+ expect(WebMock).to have_requested(request, url).with(
68
+ query: params
69
+ )
70
+ end
71
+ end
72
+
73
+ shared_examples 'an api method with optional params' do |command, args, request, endpoint, opt_params|
74
+ include_examples 'an api method', command, args, request, endpoint
75
+ it 'queries the api with optional params' do
76
+ url = api_url + endpoint
77
+ stub_request(request, /#{url}/).to_return(body: '{}').then.to_raise(StandardError)
78
+ expect(dog.send(command, *args, opt_params)).to eq ['200', {}]
79
+
80
+ opt_params.each { |k, v| opt_params[k] = v.join(',') if v.is_a? Array }
81
+ params = opt_params.merge default_query
82
+
83
+ expect(WebMock).to have_requested(request, url).with(
84
+ query: params
85
+ )
86
+ end
87
+ end
88
+ end
9
89
 
10
90
  # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
11
91
  RSpec.configure do |config|
12
- config.treat_symbols_as_metadata_keys_with_true_values = true
13
- config.run_all_when_everything_filtered = true
14
- config.filter_run :focus
15
-
92
+ # Global let
93
+ config.include SpecDog
16
94
  # Run specs in random order to surface order dependencies. If you find an
17
95
  # order dependency and want to debug it, you can fix the order by providing
18
96
  # the seed, which is printed after each run.
19
97
  # --seed 1234
20
98
  config.order = 'random'
21
99
  end
22
-
23
- VCR.configure do |c|
24
- c.cassette_library_dir = 'spec/support/cassettes'
25
- c.configure_rspec_metadata!
26
- c.default_cassette_options = { :record => :new_episodes, :re_record_interval => 7776000 } # 90 days, in seconds
27
- c.hook_into :webmock
28
- c.filter_sensitive_data('<DATADOG_API_KEY>') { ENV["DATADOG_API_KEY"] }
29
- c.filter_sensitive_data('<DATADOG_APP_KEY>') { ENV["DATADOG_APP_KEY"] }
30
- end
@@ -0,0 +1,101 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe 'Common' do
4
+ context 'Scope' do
5
+ it 'validates the Scope class' do
6
+ obj = Dogapi::Scope.new('somehost', 'somedevice')
7
+
8
+ expect(obj.host).to eq 'somehost'
9
+ expect(obj.device).to eq 'somedevice'
10
+ end
11
+ end # end Scope
12
+
13
+ context 'HttpConnection' do
14
+ it 'respects the proxy configuration' do
15
+ service = Dogapi::APIService.new('api_key', 'app_key')
16
+
17
+ service.connect do |conn|
18
+ expect(conn.proxy_address).to be(nil)
19
+ expect(conn.proxy_port).to be(nil)
20
+ end
21
+
22
+ ENV['http_proxy'] = 'https://www.proxy.com:443'
23
+
24
+ service.connect do |conn|
25
+ expect(conn.proxy_address).to eq 'www.proxy.com'
26
+ expect(conn.proxy_port).to eq 443
27
+ end
28
+
29
+ ENV['http_proxy'] = nil
30
+ end
31
+
32
+ it 'respects the endpoint configuration' do
33
+ service = Dogapi::APIService.new('api_key', 'app_key', true, nil, 'https://app.example.com')
34
+
35
+ service.connect do |conn|
36
+ expect(conn.address).to eq 'app.example.com'
37
+ expect(conn.port).to eq 443
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ class FakeResponse
44
+ attr_accessor :code, :body
45
+ def initialize(code, body)
46
+ # Instance variables
47
+ @code = code
48
+ @body = body
49
+ end
50
+ end
51
+
52
+ describe Dogapi::APIService do
53
+ let(:dogapi_service_silent) { Dogapi::APIService.new 'API_KEY', 'APP_KEY' }
54
+ let(:dogapi_service) { Dogapi::APIService.new 'API_KEY', 'APP_KEY', false }
55
+ let(:std_error) { StandardError.new('test3') }
56
+
57
+ describe '#suppress_error_if_silent' do
58
+ context 'when silent' do
59
+ it "doesn't raise an error" do
60
+ dog = dogapi_service_silent
61
+ expect { dog.suppress_error_if_silent(std_error) }.not_to raise_error
62
+ expect { dog.suppress_error_if_silent(std_error) }.to output("test3\n").to_stderr
63
+ expect(dog.suppress_error_if_silent(std_error)).to eq([-1, {}])
64
+ end
65
+ end
66
+ context 'when not silent' do
67
+ it 'raises an error' do
68
+ dog = dogapi_service
69
+ expect { dog.suppress_error_if_silent(std_error) }.to raise_error(std_error)
70
+ end
71
+ end
72
+ end
73
+
74
+ describe '#handle_response' do
75
+ context 'when receiving a correct reponse with valid json' do
76
+ it 'parses it and return code and parsed body' do
77
+ dog = dogapi_service
78
+ resp = FakeResponse.new 202, '{"test2": "test3"}'
79
+ expect(dog.handle_response(resp)).to eq([202, { 'test2' => 'test3' }])
80
+ end
81
+ end
82
+ context 'when receiving a response with invalid json' do
83
+ it 'raises an error' do
84
+ dog = dogapi_service
85
+ resp = FakeResponse.new 202, "{'test2': }"
86
+ expect { dog.handle_response(resp) }.to raise_error(RuntimeError, "Invalid JSON Response: {'test2': }")
87
+ end
88
+ end
89
+ context 'when receiving a bad response' do
90
+ it 'returns the error code and an empty body' do
91
+ dog = dogapi_service
92
+ resp = FakeResponse.new 204, ''
93
+ expect(dog.handle_response(resp)).to eq([204, {}])
94
+ resp = FakeResponse.new 202, nil
95
+ expect(dog.handle_response(resp)).to eq([202, {}])
96
+ resp = FakeResponse.new 202, 'null'
97
+ expect(dog.handle_response(resp)).to eq([202, {}])
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,79 @@
1
+ require 'spec_helper'
2
+
3
+ describe Dogapi::Client do
4
+ before(:each) do
5
+ @dogmock = Dogapi::Client.new(api_key, app_key)
6
+ @service = @dogmock.instance_variable_get(:@metric_svc)
7
+ @service.instance_variable_set(:@uploaded, [])
8
+ def @service.upload(payload)
9
+ @uploaded << payload
10
+ [200, {}]
11
+ end
12
+ end
13
+
14
+ describe '#emit_point' do
15
+ it 'passes data' do
16
+ @dogmock.emit_point('metric.name', 0, host: 'myhost')
17
+
18
+ uploaded = @service.instance_variable_get(:@uploaded)
19
+ expect(uploaded.length).to eq 1
20
+ series = uploaded.first
21
+ expect(series.class).to eq Array
22
+ expect(series[0][:metric]).to eq 'metric.name'
23
+ expect(series[0][:points][0][1]).to eq 0
24
+ expect(series[0][:host]).to eq 'myhost'
25
+ end
26
+
27
+ it 'uses localhost default' do
28
+ @dogmock.emit_point('metric.name', 0)
29
+
30
+ uploaded = @service.instance_variable_get(:@uploaded)
31
+ series = uploaded.first
32
+ expect(series[0][:host]).to eq Dogapi.find_localhost
33
+ end
34
+
35
+ it 'can pass nil host' do
36
+ @dogmock.emit_point('metric.name', 0, host: nil)
37
+
38
+ uploaded = @service.instance_variable_get(:@uploaded)
39
+ series = uploaded.first
40
+ expect(series[0][:host]).to be_nil
41
+ end
42
+
43
+ it 'can be batched' do
44
+ code, = @dogmock.batch_metrics do
45
+ @dogmock.emit_point('metric.name', 1, type: 'counter')
46
+ @dogmock.emit_point('othermetric.name', 2, type: 'counter')
47
+ end
48
+ expect(code).to eq 200
49
+ # Verify that we uploaded what we expected
50
+ uploaded = @service.instance_variable_get(:@uploaded)
51
+ expect(uploaded.length).to eq 1
52
+ series = uploaded.first
53
+ expect(series.class).to eq Array
54
+ expect(series[0][:metric]).to eq 'metric.name'
55
+ expect(series[0][:points][0][1]).to eq 1
56
+ expect(series[0][:type]).to eq 'counter'
57
+ expect(series[1][:metric]).to eq 'othermetric.name'
58
+ expect(series[1][:points][0][1]).to eq 2
59
+ expect(series[1][:type]).to eq 'counter'
60
+
61
+ # Verify that the buffer was correclty emptied
62
+ buffer = @service.instance_variable_get(:@buffer)
63
+ expect(buffer).to be nil
64
+ end
65
+
66
+ it 'flushes the buffer even if an exception is raised' do
67
+ buffer = []
68
+ begin
69
+ @dogmock.batch_metrics do
70
+ @dogmock.emit_point('metric.name', 1, type: 'counter')
71
+ raise 'Oh no, something went wrong'
72
+ end
73
+ rescue
74
+ buffer = @service.instance_variable_get(:@buffer)
75
+ end
76
+ expect(buffer).to be nil
77
+ end
78
+ end
79
+ end