wavefront-sdk 3.7.1 → 5.2.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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +43 -2
  3. data/.travis.yml +0 -1
  4. data/HISTORY.md +33 -0
  5. data/README.md +4 -3
  6. data/lib/wavefront-sdk/account.rb +104 -3
  7. data/lib/wavefront-sdk/api_mixins/user.rb +10 -0
  8. data/lib/wavefront-sdk/cloudintegration.rb +27 -0
  9. data/lib/wavefront-sdk/core/api_caller.rb +50 -7
  10. data/lib/wavefront-sdk/core/exception.rb +6 -0
  11. data/lib/wavefront-sdk/credentials.rb +28 -9
  12. data/lib/wavefront-sdk/defs/version.rb +1 -3
  13. data/lib/wavefront-sdk/paginator/base.rb +21 -15
  14. data/lib/wavefront-sdk/query.rb +0 -1
  15. data/lib/wavefront-sdk/role.rb +128 -0
  16. data/lib/wavefront-sdk/spy.rb +126 -0
  17. data/lib/wavefront-sdk/stdlib/array.rb +1 -1
  18. data/lib/wavefront-sdk/stdlib/time.rb +13 -0
  19. data/lib/wavefront-sdk/unstable/README.md +4 -0
  20. data/lib/wavefront-sdk/unstable/chart.rb +90 -0
  21. data/lib/wavefront-sdk/unstable/unstable.rb +9 -0
  22. data/lib/wavefront-sdk/user.rb +31 -0
  23. data/lib/wavefront-sdk/usergroup.rb +17 -16
  24. data/lib/wavefront-sdk/validators.rb +52 -7
  25. data/lib/wavefront-sdk/write.rb +9 -9
  26. data/spec/.rubocop.yml +41 -0
  27. data/spec/spec_helper.rb +4 -0
  28. data/spec/support/minitest_assertions.rb +4 -4
  29. data/spec/wavefront-sdk/account_spec.rb +107 -1
  30. data/spec/wavefront-sdk/cloudintegration_spec.rb +38 -0
  31. data/spec/wavefront-sdk/core/api_caller_spec.rb +43 -0
  32. data/spec/wavefront-sdk/credentials_spec.rb +3 -4
  33. data/spec/wavefront-sdk/metric_helper_spec.rb +1 -1
  34. data/spec/wavefront-sdk/role_spec.rb +96 -0
  35. data/spec/wavefront-sdk/spy_spec.rb +113 -0
  36. data/spec/wavefront-sdk/unstable/chart_spec.rb +39 -0
  37. data/spec/wavefront-sdk/user_spec.rb +8 -0
  38. data/spec/wavefront-sdk/usergroup_spec.rb +21 -11
  39. data/spec/wavefront-sdk/validators_spec.rb +31 -0
  40. data/wavefront-sdk.gemspec +8 -8
  41. metadata +32 -21
@@ -1,6 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'pathname'
4
-
5
- WF_SDK_VERSION = '3.7.1'
3
+ WF_SDK_VERSION = '5.2.0'
6
4
  WF_SDK_LOCATION = Pathname.new(__dir__).parent.parent.parent
@@ -5,23 +5,29 @@ require_relative '../defs/constants'
5
5
  module Wavefront
6
6
  module Paginator
7
7
  #
8
- # Automatically handle pagination. This is an abstract class
9
- # made concrete by an extension for each HTTP request type.
8
+ # Automatically handle pagination. This is an abstract class made concrete
9
+ # by an extension for each HTTP request type.
10
10
  #
11
- # This class and its children do slightly unpleasant things with
12
- # the HTTP request passed to us by the user, extracting and
13
- # changing values in the URI, query string, or POST/PUT body.
14
- # The POST class is particularly onerous.
11
+ # This class and its children do slightly unpleasant things with the HTTP
12
+ # request passed to us by the user, extracting and changing values in the
13
+ # URI, query string, or POST/PUT body. The POST class is particularly
14
+ # onerous.
15
15
  #
16
- # Automatic pagination works by letting the user override the
17
- # limit and offset values in API calls. Setting the limit to
18
- # :all iteratively calls the Wavefront API, returning all
19
- # requested objects an a standard Wavefront::Response wrapper;
20
- # setting limit to :lazy returns a lazy Enumerable. The number
21
- # of objects fetched in each API call, whether eager or lazy
22
- # defaults to PAGE_SIZE, but the user can override that value by
23
- # using the offset argument in conjunction with limit = :lazy |
24
- # :all.
16
+ # Automatic pagination works by letting the user override the limit and
17
+ # offset values in API calls.
18
+ #
19
+ # * Calling with limit = :all iteratively calls the Wavefront API,
20
+ # returning all requested objects in a standard Wavefront::Response
21
+ # wrapper.
22
+ #
23
+ # * Calling with limit = :lazy returns a lazy Enumerable.
24
+ #
25
+ # The number of objects fetched in each API call, eager or lazy, defaults
26
+ # to PAGE_SIZE, but the user can override that value by using the offset
27
+ # argument in conjunction with limit = :lazy | :all.
28
+ #
29
+ # So, for example, to fetch all objects in blocks of ten (which could
30
+ # require a lot of API calls) you would use { limit: :all, offset: 10 }
25
31
  #
26
32
  class Base
27
33
  attr_reader :api_caller, :conn, :method, :args, :page_size,
@@ -43,7 +43,6 @@ module Wavefront
43
43
  options[:s] = parse_time(t_start, true)
44
44
  options[:e] = parse_time(t_end, true) if t_end
45
45
 
46
- options.delete_if { |k, v| v == false && k != :i }
47
46
  api.get('api', options)
48
47
  end
49
48
 
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'core/api'
4
+ require_relative 'api_mixins/user'
5
+
6
+ module Wavefront
7
+ #
8
+ # Manage and query Wavefront roles
9
+ #
10
+ class Role < CoreApi
11
+ include Wavefront::Mixin::User
12
+
13
+ def update_keys
14
+ %i[id name description]
15
+ end
16
+
17
+ # GET /api/v2/role
18
+ # Get all roles for a customer
19
+ # @param offset [Int] alert at which the list begins
20
+ # @param limit [Int] the number of alerts to return
21
+ # @return [Wavefront::Response]
22
+ #
23
+ def list(offset = 0, limit = 100)
24
+ api.get('', offset: offset, limit: limit)
25
+ end
26
+
27
+ # POST /api/v2/role
28
+ # Create a specific role
29
+ # @param body [Hash] a hash of parameters describing the role. Please
30
+ # refer to the Wavefront Swagger docs for key:value information
31
+ # @return [Wavefront::Response]
32
+ #
33
+ def create(body)
34
+ raise ArgumentError unless body.is_a?(Hash)
35
+
36
+ api.post('', body, 'application/json')
37
+ end
38
+
39
+ # DELETE /api/v2/role/{id}
40
+ # Delete a specific role
41
+ # @param id [String] ID of the role
42
+ # @return [Wavefront::Response]
43
+ #
44
+ def delete(id)
45
+ wf_role_id?(id)
46
+ api.delete(id)
47
+ end
48
+
49
+ # GET /api/v2/role/{id}
50
+ # Get a specific role
51
+ # @param id [String] ID of the role
52
+ # @return [Wavefront::Response]
53
+ #
54
+ def describe(id)
55
+ wf_role_id?(id)
56
+ api.get(id)
57
+ end
58
+
59
+ # PUT /api/v2/role/{id}
60
+ # Update a specific role
61
+ # @param id [String] role ID
62
+ # @param body [Hash] key-value hash of the parameters you wish to change
63
+ # @param modify [true, false] if true, use {#describe()} to get a hash
64
+ # describing the existing object, and modify that with the new body. If
65
+ # false, pass the new body straight through.
66
+ # @return [Wavefront::Response]
67
+ #
68
+ def update(id, body, modify = true)
69
+ wf_role_id?(id)
70
+ raise ArgumentError unless body.is_a?(Hash)
71
+
72
+ return api.put(id, body, 'application/json') unless modify
73
+
74
+ api.put(id, hash_for_update(describe(id).response, body),
75
+ 'application/json')
76
+ end
77
+
78
+ # POST /api/v2/role/{id}/addAssignees
79
+ # Add multiple users and user groups to a specific role
80
+ # @param id [String] role ID
81
+ # @param assignees [Array[String]] list of roles or accounts to be added
82
+ # @return [Wavefront::Response]
83
+ #
84
+ def add_assignees(id, assignees)
85
+ wf_role_id?(id)
86
+ validate_user_list(assignees)
87
+ api.post([id, 'addAssignees'].uri_concat, assignees, 'application/json')
88
+ end
89
+
90
+ # POST /api/v2/role/{id}/removeAssignees
91
+ # Remove multiple users and user groups from a specific role
92
+ # @param id [String] role ID
93
+ # @param assignees [Array[String]] list of roles or accounts to be removed
94
+ # @return [Wavefront::Response]
95
+ #
96
+ def remove_assignees(id, assignees)
97
+ wf_role_id?(id)
98
+ validate_user_list(assignees)
99
+ api.post([id, 'removeAssignees'].uri_concat,
100
+ assignees,
101
+ 'application/json')
102
+ end
103
+
104
+ # POST /api/v2/role/grant/{permission}
105
+ # Grants a single permission to role(s)
106
+ # @param permission [String] permission to grant
107
+ # @param roles [Array[String]] list of roles to receive permission
108
+ # @return [Wavefront::Response]
109
+ #
110
+ def grant(permission, roles)
111
+ wf_permission?(permission)
112
+ validate_role_list(roles)
113
+ api.post(['grant', permission].uri_concat, roles, 'application/json')
114
+ end
115
+
116
+ # POST /api/v2/role/revoke/{permission}
117
+ # Revokes a single permission from role(s)
118
+ # @param permission [String] permission to revoke
119
+ # @param roles [Array[String]] list of roles to lose permission
120
+ # @return [Wavefront::Response]
121
+ #
122
+ def revoke(permission, roles)
123
+ wf_permission?(permission)
124
+ validate_role_list(roles)
125
+ api.post(['revoke', permission].uri_concat, roles, 'application/json')
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'defs/constants'
4
+ require_relative 'core/api'
5
+
6
+ module Wavefront
7
+ #
8
+ # Spy on data going into Wavefront
9
+ #
10
+ class Spy < CoreApi
11
+ # GET /api/spy/points
12
+ # Gets new metric data points that are added to existing time series.
13
+ # @param sampling [Float] the amount of points to sample, from 0
14
+ # (none) to 1 (all)
15
+ # @param filter [Hash] with the following keys:
16
+ # :prefix [String] only list points whose metric name begins with this
17
+ # case-sensitive string
18
+ # :host [Array] only list points if source name begins with this
19
+ # case-sensitive string
20
+ # :tag_key [String,Array[String]] only list points with one or more of
21
+ # the given points tags
22
+ # @param options [Hash] with the following keys
23
+ # :timestamp [Boolean] prefix each block of streamed data with a
24
+ # timestamp
25
+ # :timeout [Integer] how many seconds to run the spy. After this time
26
+ # the method returns
27
+ # @raise Wavefront::Exception::InvalidSamplingValue
28
+ # @return [Nil]
29
+ #
30
+ def points(sampling = 0.01, filters = {}, options = {})
31
+ wf_sampling_value?(sampling)
32
+ api.get_stream('points', points_filter(sampling, filters), options)
33
+ end
34
+
35
+ # GET /api/spy/histograms
36
+ # Gets new histograms that are added to existing time series.
37
+ # @param sampling [Float] see #points
38
+ # @param filter [Hash] see #points
39
+ # @param options [Hash] see #points
40
+ # @raise Wavefront::Exception::InvalidSamplingValue
41
+ # @return [Nil]
42
+ #
43
+ def histograms(sampling = 0.01, filters = {}, options = {})
44
+ wf_sampling_value?(sampling)
45
+ api.get_stream('histograms',
46
+ histograms_filter(sampling, filters),
47
+ options)
48
+ end
49
+
50
+ # GET /api/spy/spans
51
+ # Gets new spans with existing source names and span tags.
52
+ # @param sampling [Float] see #points
53
+ # @param filter [Hash] see #points
54
+ # @param options [Hash] see #points
55
+ # @raise Wavefront::Exception::InvalidSamplingValue
56
+ # @return [Nil]
57
+ #
58
+ def spans(sampling = 0.01, filters = {}, options = {})
59
+ wf_sampling_value?(sampling)
60
+ api.get_stream('spans', spans_filter(sampling, filters), options)
61
+ end
62
+
63
+ # GET /api/spy/ids
64
+ # Gets newly allocated IDs that correspond to new metric names, source
65
+ # names, point tags, or span tags. A new ID generally indicates that a
66
+ # new time series has been introduced.
67
+ # @param sampling [Float] see #points
68
+ # @param filter [Hash] with keys:
69
+ # :prefix [String] only list assignments whose metric name begins with
70
+ # this case-sensitive string
71
+ # :type [String] one of METRIC, SPAN, HOST or STRING
72
+ # @param options [Hash] see #points
73
+ #
74
+ def ids(sampling = 0.01, filters = {}, options = {})
75
+ wf_sampling_value?(sampling)
76
+ api.get_stream('ids', ids_filter(sampling, filters), options)
77
+ end
78
+
79
+ def api_path
80
+ '/api/spy'
81
+ end
82
+
83
+ # We have to try to make the response we get from the API look
84
+ # like the one we get from the public API. To begin with, it's
85
+ # nothing like it.
86
+ #
87
+ # This method must be public because a #respond_to? looks for
88
+ # it.
89
+ #
90
+ def _response_shim(resp, status)
91
+ { response: parse_response(resp),
92
+ status: { result: status == 200 ? 'OK' : 'ERROR',
93
+ message: extract_api_message(status, resp),
94
+ code: status } }.to_json
95
+ end
96
+
97
+ private
98
+
99
+ def points_filter(sampling, filters)
100
+ { metric: filters.fetch(:prefix, nil),
101
+ host: filters.fetch(:host, nil),
102
+ sampling: sampling,
103
+ pointTagKey: filters.fetch(:tag_key, nil) }.compact
104
+ end
105
+
106
+ def histograms_filter(sampling, filters)
107
+ { histogram: filters.fetch(:prefix, nil),
108
+ host: filters.fetch(:host, nil),
109
+ sampling: sampling,
110
+ histogramTagKey: filters.fetch(:tag_key, nil) }.compact
111
+ end
112
+
113
+ def spans_filter(sampling, filters)
114
+ { name: filters.fetch(:prefix, nil),
115
+ host: filters.fetch(:host, nil),
116
+ sampling: sampling,
117
+ spanTagKey: filters.fetch(:tag_key, nil) }.compact
118
+ end
119
+
120
+ def ids_filter(sampling, filters)
121
+ { name: filters.fetch(:prefix, nil),
122
+ type: filters.fetch(:type, nil),
123
+ sampling: sampling }.compact
124
+ end
125
+ end
126
+ end
@@ -11,6 +11,6 @@ class Array
11
11
  # @return [String] a URI path
12
12
  #
13
13
  def uri_concat
14
- join('/').squeeze('/').sub(%r{\/$}, '').sub(%r{\/\?}, '?')
14
+ join('/').squeeze('/').sub(%r{/$}, '').sub(%r{/\?}, '?')
15
15
  end
16
16
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Extensions to the stdlib Time class
4
+ #
5
+ class Time
6
+ #
7
+ # The real hi-res time. See
8
+ # https://blog.dnsimple.com/2018/03/elapsed-time-with-ruby-the-right-way/
9
+ #
10
+ def self.right_now
11
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
12
+ end
13
+ end
@@ -0,0 +1,4 @@
1
+ The classes in here use undocumented APIs. They are thus subject to
2
+ change or revocation at any time.
3
+
4
+ Use them at your own risk.
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../defs/constants'
4
+ require_relative '../core/api'
5
+
6
+ module Wavefront
7
+ module Unstable
8
+ #
9
+ # This is an unstable class. Please refer to README.md.
10
+ #
11
+ class Chart < CoreApi
12
+ def all_metrics
13
+ metrics_under('')
14
+ end
15
+
16
+ # Gets a list of metrics under the given path. This must be done via
17
+ # recursive calls to the API, so calls can take a while. If you ask for
18
+ # all your metrics, expect to be waiting some time.
19
+ #
20
+ # @return [Wavefront::Response]
21
+ #
22
+ # rubocop:disable Metrics/MethodLength
23
+ # rubocop:disable Metrics/AbcSize
24
+ def metrics_under(path, cursor = nil, limit = 100)
25
+ resp = api.get('metrics/all',
26
+ { trie: true, q: path, p: cursor, l: limit }.compact)
27
+
28
+ return resp unless resp.ok?
29
+
30
+ metrics = resp.response.items
31
+
32
+ metrics.each do |m|
33
+ if m.end_with?('.')
34
+ metrics += metrics_under(m).response.items
35
+ metrics.delete(m)
36
+ end
37
+ end
38
+
39
+ # resp.more_items? doesn't work: we don't get that from this API
40
+
41
+ if metrics.size == limit
42
+ metrics += metrics_under(path, metrics.last, limit).response.items
43
+ end
44
+
45
+ resp.response.items = metrics.sort
46
+ resp
47
+ end
48
+ # rubocop:enable Metrics/MethodLength
49
+ # rubocop:enable Metrics/AbcSize
50
+
51
+ def api_path
52
+ '/chart'
53
+ end
54
+
55
+ # We have to try to make the response we get from the API look
56
+ # like the one we get from the public API. To begin with, it's
57
+ # nothing like it.
58
+ #
59
+ # This method must be public because a #respond_to? looks for
60
+ # it.
61
+ #
62
+ def response_shim(resp, status)
63
+ { response: parse_response(resp),
64
+ status: { result: status == 200 ? 'OK' : 'ERROR',
65
+ message: extract_api_message(status, resp),
66
+ code: status } }.to_json
67
+ end
68
+
69
+ private
70
+
71
+ def parse_response(resp)
72
+ metrics = JSON.parse(resp, symbolize_names: true)[:metrics]
73
+
74
+ { items: metrics,
75
+ offset: 0,
76
+ limit: metrics.size,
77
+ totalItems: metrics.size,
78
+ moreItems: false }
79
+ rescue JSON::ParserError
80
+ nil
81
+ end
82
+
83
+ def extract_api_message(_status, resp)
84
+ resp.match(/^message='(.*)'/)[1]
85
+ rescue NoMethodError
86
+ ''
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wavefront
4
+ #
5
+ # Placeholder for unstable API classes
6
+ #
7
+ module Unstable
8
+ end
9
+ end