dogapi 1.22.0 → 1.23.0

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.
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