grafana 0.8.5 → 1.1.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 (49) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +8 -3
  3. data/lib/_logging.rb_ +55 -0
  4. data/lib/grafana.rb +1 -1
  5. data/lib/grafana/admin.rb +2 -1
  6. data/lib/grafana/alerts.rb +338 -14
  7. data/lib/grafana/annotations.rb +284 -9
  8. data/lib/grafana/auth.rb +150 -0
  9. data/lib/grafana/client.rb +81 -5
  10. data/lib/grafana/dashboard.rb +99 -7
  11. data/lib/grafana/dashboard_permissions.rb +132 -0
  12. data/lib/grafana/dashboard_versions.rb +101 -5
  13. data/lib/grafana/datasource.rb +34 -38
  14. data/lib/grafana/folder.rb +198 -0
  15. data/lib/grafana/folder_and_dashboard_search.rb +57 -0
  16. data/lib/grafana/folder_permissions.rb +155 -0
  17. data/lib/grafana/logging.rb +55 -0
  18. data/lib/grafana/login.rb +93 -49
  19. data/lib/grafana/network.rb +130 -101
  20. data/lib/grafana/organization.rb +2 -1
  21. data/lib/grafana/organizations.rb +15 -6
  22. data/lib/grafana/playlist.rb +594 -0
  23. data/lib/grafana/preferences.rb +122 -0
  24. data/lib/grafana/tags.rb +16 -0
  25. data/lib/grafana/teams.rb +364 -0
  26. data/lib/grafana/tools.rb +42 -9
  27. data/lib/grafana/user.rb +6 -2
  28. data/lib/grafana/users.rb +19 -11
  29. data/lib/grafana/validator.rb +47 -2
  30. data/lib/grafana/version.rb +3 -3
  31. metadata +16 -39
  32. data/doc/Array.html +0 -200
  33. data/doc/Boolean.html +0 -122
  34. data/doc/FalseClass.html +0 -132
  35. data/doc/Grafana.html +0 -172
  36. data/doc/Hash.html +0 -212
  37. data/doc/Logging.html +0 -326
  38. data/doc/Object.html +0 -286
  39. data/doc/Time.html +0 -200
  40. data/doc/TrueClass.html +0 -132
  41. data/doc/_index.html +0 -380
  42. data/doc/class_list.html +0 -51
  43. data/doc/file.README.html +0 -117
  44. data/doc/file_list.html +0 -56
  45. data/doc/frames.html +0 -17
  46. data/doc/index.html +0 -117
  47. data/doc/method_list.html +0 -747
  48. data/doc/top-level-namespace.html +0 -112
  49. data/lib/logging.rb +0 -35
@@ -3,179 +3,208 @@ module Grafana
3
3
 
4
4
  module Network
5
5
 
6
+ # GET request
7
+ #
8
+ # @param endpoint [String]
9
+ #
6
10
  def get( endpoint )
7
-
8
11
  request( 'GET', endpoint )
9
12
  end
10
13
 
14
+ # POST request
15
+ #
16
+ # @param endpoint [String]
17
+ # @param data [Hash]
18
+ #
11
19
  def post( endpoint, data )
12
-
13
20
  request( 'POST', endpoint, data )
14
21
  end
15
22
 
23
+ # PUT request
24
+ #
25
+ # @param endpoint [String]
26
+ # @param data [Hash]
27
+ #
16
28
  def put( endpoint, data )
17
-
18
29
  request( 'PUT', endpoint, data )
19
30
  end
20
31
 
32
+ # PATCH request
33
+ #
34
+ # @param endpoint [String]
35
+ # @param data [Hash]
36
+ #
21
37
  def patch( endpoint, data )
22
-
23
38
  request( 'PATCH', endpoint, data )
24
39
  end
25
40
 
41
+ # DELETE request
42
+ #
43
+ # @param endpoint [String]
44
+ #
26
45
  def delete( endpoint )
27
-
28
46
  request( 'DELETE', endpoint )
29
47
  end
30
48
 
49
+
50
+ private
51
+ # helper function for all request methods
52
+ #
53
+ # @param method_type [String]
54
+ # @param endpoint [String]
55
+ # @param data [Hash]
56
+ #
57
+ # @example
58
+ #
59
+ #
60
+ # @return [Hash]
61
+ #
31
62
  def request( method_type = 'GET', endpoint = '/', data = {} )
32
63
 
33
- raise 'try first login()' if @api_instance.nil?
64
+ logger.debug( "request( method_type: #{method_type}, endpoint: #{endpoint}, data )" )
65
+
66
+ raise 'try first login()' if @api_instance.nil?
67
+
68
+ # login( username: @username, password: @password )
34
69
 
35
70
  response = nil
36
71
  response_code = 404
37
72
  response_body = ''
38
73
 
39
74
  begin
40
-
41
75
  case method_type.upcase
42
76
  when 'GET'
43
- response = @api_instance[endpoint].get( @headers )
77
+ response = @api_instance[endpoint].get( headers )
44
78
  when 'POST'
45
- response = @api_instance[endpoint].post( data, @headers )
79
+ response = @api_instance[endpoint].post( data, headers )
46
80
  when 'PATCH'
47
- response = @api_instance[endpoint].patch( data, @headers )
81
+ response = @api_instance[endpoint].patch( data, headers )
48
82
  when 'PUT'
49
- # response = @api_instance[endpoint].put( data, @headers )
50
- @api_instance[endpoint].put( data, @headers ) do |response, request, result|
51
83
 
52
- case response.code
84
+ # response = @api_instance[endpoint].put( data, headers )
85
+ @api_instance[endpoint].put( data, headers ) do |resp, _request, _result|
86
+
87
+ response_code = resp.code.to_i
88
+ response_body = resp.body
89
+ response_body = JSON.parse(response_body) if response_body.is_a?(String)
90
+
91
+ #logger.debug( "code : #{response_code}" )
92
+ #logger.debug( "message: #{response_body}" )
93
+
94
+ case response_code.to_i
53
95
  when 200
54
- response_body = response.body
55
- response_code = response.code.to_i
56
- response_body = JSON.parse(response_body) if response_body.is_a?(String)
57
-
58
- return {
59
- 'status' => response_code,
60
- 'message' => response_body.dig('message').nil? ? 'Successful' : response_body.dig('message')
61
- }
96
+ return { 'status' => response_code, 'message' => response_body.dig('message').nil? ? 'Successful' : response_body.dig('message') }
62
97
  when 400
63
- response_body = response.body
64
- response_code = response.code.to_i
65
98
  raise RestClient::BadRequest
99
+ when 412
100
+ status = response_body.dig('status')
101
+ message = response_body.dig('message')
102
+ message += " (#{status})" unless(status.nil?)
103
+ return { 'status' => response_code, 'message' => message }
104
+ when 422
105
+ logger.error('422')
106
+
107
+ response_body = response_body.first if(response_body.is_a?(Array))
108
+ # message_field_name = response_body.dig('fieldNames')
109
+
110
+ #status = response_code # response_body.dig('status')
111
+ message = response_body # .dig('message')
112
+ #message += " (#{status})" unless(status.nil?)
113
+
114
+ # [{fieldNames"=>["Id"], "classification"=>"RequiredError", "message"=>"Required"}]
115
+
116
+ logger.error(message)
117
+ return { 'status' => response_code, 'message' => message }
118
+ # #raise RestClient::UnprocessableEntity
66
119
  else
67
- # logger.error( response.code )
68
- # logger.error( response.body )
69
-
70
- body = JSON.parse(response.body) if(response_body.is_a?(String))
71
- return {
72
- 'status' => response_code,
73
- 'message' => body.dig('message')
74
- }
120
+ # logger.error( response_code )
121
+ # logger.error( response_body )
122
+ return { 'status' => response_code, 'message' => response_body.dig('message') }
75
123
  # response.return! # (request, result)
76
124
  end
77
125
  end
78
126
 
79
127
  when 'DELETE'
80
- response = @api_instance[endpoint].delete( @headers )
128
+
129
+ @api_instance[endpoint].delete( headers ) do |resp, _request, _result|
130
+
131
+ response_code = resp.code.to_i
132
+ response_body = resp.body
133
+ response_body = JSON.parse(response_body) if response_body.is_a?(String)
134
+
135
+ #logger.debug( "code : #{response_code}" )
136
+ #logger.debug( "message: #{response_body}" )
137
+
138
+ case response_code.to_i
139
+ when 200
140
+ return { 'status' => response_code, 'message' => response_body.dig('message').nil? ? 'Successful' : response_body.dig('message') }
141
+ when 404
142
+ return { 'status' => response_code, 'message' => response_body.dig('message').nil? ? 'Successful' : response_body.dig('message') }
143
+ else
144
+ # logger.error( response_code )
145
+ # logger.error( response_body )
146
+ return { 'status' => response_code, 'message' => response_body.dig('message') }
147
+ end
148
+
149
+ end
150
+
81
151
  else
82
- @logger.error( "Error: #{__method__} is not a valid request method." )
152
+ logger.error( "Error: #{__method__} is not a valid request method." )
83
153
  return false
84
154
  end
85
155
 
156
+ # logger.debug( "response: #{response} (#{response.class})" )
157
+
86
158
  response_code = response.code.to_i
87
159
  response_body = response.body
88
160
  response_headers = response.headers
89
161
 
90
- if( @debug )
91
- logger.debug("response_code : #{response_code}" )
92
- logger.debug("response_body : #{response_body}" )
93
- logger.debug("response_headers : #{response_headers}" )
94
- end
95
-
96
162
  if( ( response_code >= 200 && response_code <= 299 ) || ( response_code >= 400 && response_code <= 499 ) )
97
163
 
98
- result = JSON.parse( response_body )
99
-
100
- if( result.is_a?(Array) )
101
- r_result= {
102
- 'status' => response_code,
103
- 'message' => result
104
- }
105
- return r_result
164
+ if( response_body =~ /^\[.*\]$/ || response_body =~ /^\{.*\}$/ )
165
+ result = JSON.parse( response_body )
166
+ return { 'status' => response_code, 'message' => result } if( result.is_a?(Array) )
167
+ else
168
+ return { 'status' => response_code, 'message' => response_body }
106
169
  end
107
170
 
108
- result_status = result.dig('status') if( result.is_a?( Hash ) )
109
-
171
+ result_status = result.dig('status') if( result.is_a?( Hash ) )
110
172
  result['message'] = result_status unless( result_status.nil? )
111
173
  result['status'] = response_code
112
174
 
113
175
  return result
114
176
  else
115
-
116
- @logger.error( "#{__method__} #{method_type.upcase} on #{endpoint} failed: HTTP #{response.code} - #{response_body}" )
117
- @logger.error( @headers )
118
- @logger.error( JSON.pretty_generate( response_headers ) )
177
+ logger.error( "#{__method__} #{method_type.upcase} on #{endpoint} failed: HTTP #{response.code} - #{response_body}" )
178
+ logger.error( headers )
179
+ logger.error( JSON.pretty_generate( response_headers ) )
119
180
 
120
181
  return JSON.parse( response_body )
121
182
  end
122
183
 
123
184
  rescue RestClient::BadRequest
124
-
125
185
  response_body = JSON.parse(response_body) if response_body.is_a?(String)
126
-
127
- return {
128
- 'status' => 400,
129
- 'message' => response_body.dig('message').nil? ? 'Bad Request' : response_body.dig('message')
130
- }
131
-
186
+ return { 'status' => 400, 'message' => response_body.dig('message').nil? ? 'Bad Request' : response_body.dig('message') }
132
187
  rescue RestClient::Unauthorized
133
-
134
- return {
135
- 'status' => 401,
136
- 'message' => format('Not authorized to connect \'%s/%s\' - wrong username or password?', @url, endpoint)
137
- }
188
+ return { 'status' => 401, 'message' => format('Not authorized to connect \'%s/%s\' - wrong username or password?', @url, endpoint) }
138
189
  rescue RestClient::Forbidden
139
-
140
- return {
141
- 'status' => 403,
142
- 'message' => format('The operation is forbidden \'%s/%s\'', @url, endpoint)
143
- }
144
-
190
+ return { 'status' => 403, 'message' => format('The operation is forbidden \'%s/%s\'', @url, endpoint) }
145
191
  rescue RestClient::NotFound
146
-
147
- return {
148
- 'status' => 404,
149
- 'message' => 'Not Found'
150
- }
151
-
192
+ return { 'status' => 404, 'message' => 'Not Found' }
152
193
  rescue RestClient::Conflict
153
-
154
- return {
155
- 'status' => 409,
156
- 'message' => 'Conflict with the current state of the target resource'
157
- }
158
-
194
+ return { 'status' => 409, 'message' => 'Conflict with the current state of the target resource' }
159
195
  rescue RestClient::PreconditionFailed
160
-
161
- return {
162
- 'status' => 412,
163
- 'message' => 'Precondition failed. The Object probably already exists.'
164
- }
165
-
166
- rescue RestClient::ExceptionWithResponse => e
167
-
168
- logger.error( "Error: #{__method__} #{method_type.upcase} on #{endpoint} error: '#{e}'" )
169
- logger.error( data )
170
- logger.error( @headers )
171
- logger.error( JSON.pretty_generate( response_headers ) )
172
-
173
- return false
174
-
196
+ return { 'status' => 412, 'message' => 'Precondition failed. The Object probably already exists.' }
197
+ rescue RestClient::PreconditionFailed
198
+ return { 'status' => 412, 'message' => 'Precondition failed. The Object probably already exists.' }
199
+ rescue RestClient::ExceptionWithResponse => error
200
+ #logger.error( "Error: (RestClient::ExceptionWithResponse) #{__method__} #{method_type.upcase} on #{endpoint} error: '#{error}'" )
201
+ #logger.error( "query: #{data}" )
202
+ return { 'status' => 500, 'message' => "Internal Server Error: #{error}" }
203
+ rescue => error
204
+ #logger.error( "Error: #{__method__} #{method_type.upcase} on #{endpoint} error: '#{error}'" )
205
+ #logger.error( "query: #{data}" )
206
+ return { 'status' => 500, 'message' => "Internal Server Error: #{error}" }
175
207
  end
176
-
177
208
  end
178
-
179
209
  end
180
-
181
210
  end
@@ -103,7 +103,8 @@ module Grafana
103
103
 
104
104
  if( org.is_a?(Hash) && org.dig('status').to_i == 200 )
105
105
  org = org.dig('message')
106
- return { 'status' => 404, 'message' => format('User \'%s\' are already in the organisation', login_or_email) } if( org.select { |x| x.dig('email') == login_or_email || x.dig('login') == login_or_email }.count >= 1 )
106
+ return { 'status' => 404, 'message' => format('User \'%s\' are already in the organisation', login_or_email) } \
107
+ if( org.select { |x| x.dig('email') == login_or_email || x.dig('login') == login_or_email }.count >= 1 )
107
108
  end
108
109
 
109
110
  endpoint = '/api/org/users'
@@ -23,11 +23,14 @@ module Grafana
23
23
  #
24
24
  def organization( organisation_id )
25
25
 
26
- raise ArgumentError.new(format('wrong type. user \'organisation_id\' must be an String (for an Datasource name) or an Integer (for an Datasource Id), given \'%s\'', organisation_id.class.to_s)) if( organisation_id.is_a?(String) && organisation_id.is_a?(Integer) )
26
+ if( organisation_id.is_a?(String) && organisation_id.is_a?(Integer))
27
+ raise ArgumentError.new(format('wrong type. \'organisation_id\' must be an String (for an Datasource name) ' \
28
+ 'or an Integer (for an Datasource Id), given \'%s\'', organisation_id.class.to_s))
29
+ end
27
30
  raise ArgumentError.new('missing \'organisation_id\'') if( organisation_id.size.zero? )
28
31
 
29
32
  endpoint = format( '/api/orgs/%d', organisation_id ) if(organisation_id.is_a?(Integer))
30
- endpoint = format( '/api/orgs/name/%s', URI.escape( organisation_id ) ) if(organisation_id.is_a?(String))
33
+ endpoint = format( '/api/orgs/name/%s', ERB::Util.url_encode( organisation_id ) ) if(organisation_id.is_a?(String))
31
34
 
32
35
  @logger.debug("Attempting to get existing data source Id #{organisation_id} (GET #{endpoint})") if @debug
33
36
 
@@ -83,7 +86,10 @@ module Grafana
83
86
  #
84
87
  def organization_users( organization_id )
85
88
 
86
- raise ArgumentError.new(format('wrong type. user \'organization_id\' must be an String (for an Organisation name) or an Integer (for an Organisation Id), given \'%s\'', organization_id.class.to_s)) if( organization_id.is_a?(String) && organization_id.is_a?(Integer) )
89
+ if( organization_id.is_a?(String) && organization_id.is_a?(Integer))
90
+ raise ArgumentError.new(format('wrong type. \'organization_id\' must be an String (for an Organisation name) '\
91
+ 'or an Integer (for an Organisation Id), given \'%s\'', organization_id.class.to_s))
92
+ end
87
93
  raise ArgumentError.new('missing \'organization_id\'') if( organization_id.size.zero? )
88
94
 
89
95
  if(organization_id.is_a?(String))
@@ -266,7 +272,10 @@ module Grafana
266
272
  #
267
273
  def delete_organisation( organisation_id )
268
274
 
269
- raise ArgumentError.new(format('wrong type. user \'organisation_id\' must be an String (for an Datasource name) or an Integer (for an Datasource Id), given \'%s\'', organisation_id.class.to_s)) if( organisation_id.is_a?(String) && organisation_id.is_a?(Integer) )
275
+ if( organisation_id.is_a?(String) && organisation_id.is_a?(Integer) )
276
+ raise ArgumentError.new(format('wrong type. \'organisation_id\' must be an String (for an Organisation name) ' \
277
+ 'or an Integer (for an Organisation Id), given \'%s\'', organisation_id.class.to_s))
278
+ end
270
279
  raise ArgumentError.new('missing \'organisation_id\'') if( organisation_id.size.zero? )
271
280
 
272
281
  if(organisation_id.is_a?(String))
@@ -275,7 +284,7 @@ module Grafana
275
284
  data.each do |d|
276
285
  organisation_map[d.dig('id')] = d.dig('name')
277
286
  end
278
- organisation_id = organisation_map.select { |x,y| y == organisation_id }.keys.first if( organisation_map )
287
+ organisation_id = organisation_map.select { |_,y| y == organisation_id }.keys.first if( organisation_map )
279
288
  end
280
289
 
281
290
  return { 'status' => 404, 'message' => format( 'No Organisation \'%s\' found', organisation_id) } if( organisation_id.nil? )
@@ -308,7 +317,7 @@ module Grafana
308
317
  organization = validate( params, required: true, var: 'organization', type: String )
309
318
  login_or_email = validate( params, required: true, var: 'login_or_email', type: String )
310
319
  role = validate( params, required: true, var: 'role', type: String )
311
- valid_roles = ['Viewer', 'Editor', 'Read Only Editor', 'Admin']
320
+ valid_roles = %w[Viewer Editor "Read Only Editor" Admin]
312
321
 
313
322
  # https://stackoverflow.com/questions/9333952/case-insensitive-arrayinclude?answertab=votes#tab-top
314
323
  # Do this once, or each time the array changes
@@ -0,0 +1,594 @@
1
+
2
+ module Grafana
3
+
4
+ # +++
5
+ # title = "Playlist HTTP API "
6
+ # description = "Playlist Admin HTTP API"
7
+ # keywords = ["grafana", "http", "documentation", "api", "playlist"]
8
+ # aliases = ["/http_api/playlist/"]
9
+ # type = "docs"
10
+ # [menu.docs]
11
+ # name = "Playlist"
12
+ # parent = "http_api"
13
+ # +++
14
+
15
+ # https://github.com/grafana/grafana/blob/1165d098b0d0ae705955f9d2ea104beea98ca6eb/pkg/api/dtos/playlist.go
16
+
17
+ module Playlist
18
+
19
+ ## Playlist API
20
+ #
21
+ ### Search Playlist
22
+ #
23
+ #`GET /api/playlists`
24
+ #
25
+ #Get all existing playlist for the current organization using pagination
26
+ #
27
+ #**Example Request**:
28
+ #
29
+ #```bash
30
+ #GET /api/playlists HTTP/1.1
31
+ #Accept: application/json
32
+ #Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
33
+ #```
34
+ #
35
+ # Querystring Parameters:
36
+ #
37
+ # These parameters are used as querystring parameters.
38
+ #
39
+ # - **query** - Limit response to playlist having a name like this value.
40
+ # - **limit** - Limit response to *X* number of playlist.
41
+ #
42
+ #**Example Response**:
43
+ #
44
+ #```json
45
+ #HTTP/1.1 200
46
+ #Content-Type: application/json
47
+ #[
48
+ # {
49
+ # "id": 1,
50
+ # "name": "my playlist",
51
+ # "interval": "5m"
52
+ # }
53
+ #]
54
+ #```
55
+ def playlists
56
+
57
+ endpoint = '/api/playlists'
58
+
59
+ @logger.debug("Attempting to get all existing playlists (GET #{endpoint})") if @debug
60
+
61
+ playlists = get( endpoint )
62
+
63
+ return { 'status' => 404, 'message' => 'No Playlists found' } if( playlists.nil? || playlists == false || playlists.dig('status').to_i != 200 )
64
+
65
+ playlists
66
+ end
67
+
68
+ ### Get one playlist
69
+ #
70
+ #`GET /api/playlists/:id`
71
+ #
72
+ #**Example Request**:
73
+ #
74
+ #```bash
75
+ #GET /api/playlists/1 HTTP/1.1
76
+ #Accept: application/json
77
+ #Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
78
+ #```
79
+ #
80
+ #**Example Response**:
81
+ #
82
+ #```json
83
+ #HTTP/1.1 200
84
+ #Content-Type: application/json
85
+ #{
86
+ # "id" : 1,
87
+ # "name": "my playlist",
88
+ # "interval": "5m",
89
+ # "orgId": "my org",
90
+ # "items": [
91
+ # {
92
+ # "id": 1,
93
+ # "playlistId": 1,
94
+ # "type": "dashboard_by_id",
95
+ # "value": "3",
96
+ # "order": 1,
97
+ # "title":"my third dasboard"
98
+ # },
99
+ # {
100
+ # "id": 2,
101
+ # "playlistId": 1,
102
+ # "type": "dashboard_by_tag",
103
+ # "value": "myTag",
104
+ # "order": 2,
105
+ # "title":"my other dasboard"
106
+ # }
107
+ # ]
108
+ #}
109
+ #```
110
+
111
+ def playlist( playlist_id )
112
+
113
+ if( playlist_id.is_a?(String) && playlist_id.is_a?(Integer) )
114
+ raise ArgumentError.new(format('wrong type. \'playlist_id\' must be an String (for an Playlist name) or an Integer (for an Playlist Id), given \'%s\'', playlist_id.class.to_s))
115
+ end
116
+ raise ArgumentError.new('missing \'playlist_id\'') if( playlist_id.size.zero? )
117
+
118
+ if(playlist_id.is_a?(String))
119
+
120
+ data = playlists
121
+ status = data.dig('status')
122
+ d = data.dig('message')
123
+ data = d.select { |k| k['name'] == playlist_id }
124
+
125
+ return { 'status' => 404, 'message' => format( 'No Playlist \'%s\' found', playlist_id) } if( data.size.zero? )
126
+
127
+ unless( data.empty? )
128
+ playlist_data = []
129
+ data.each_entry do |k|
130
+ playlist_data << playlist( k['id'] )
131
+ end
132
+
133
+ return { 'status' => status, 'playlists' => playlist_data }
134
+ end
135
+ end
136
+
137
+ raise format('playlist id can not be 0') if( playlist_id.zero? )
138
+
139
+ endpoint = format('/api/playlists/%d', playlist_id )
140
+
141
+ @logger.debug("Attempting to get existing playlist id #{playlist_id} (GET #{endpoint})") if @debug
142
+
143
+ result = get(endpoint)
144
+
145
+ return { 'status' => 404, 'message' => 'playlist is empty', 'items' => [] } if( result.dig('status') == 404 )
146
+
147
+ result
148
+ end
149
+
150
+ ### Get Playlist items
151
+
152
+ #`GET /api/playlists/:id/items`
153
+ #
154
+ #**Example Request**:
155
+ #
156
+ #```bash
157
+ #GET /api/playlists/1/items HTTP/1.1
158
+ #Accept: application/json
159
+ #Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
160
+ #```
161
+ #
162
+ #**Example Response**:
163
+ #
164
+ #```json
165
+ #HTTP/1.1 200
166
+ #Content-Type: application/json
167
+ #[
168
+ # {
169
+ # "id": 1,
170
+ # "playlistId": 1,
171
+ # "type": "dashboard_by_id",
172
+ # "value": "3",
173
+ # "order": 1,
174
+ # "title":"my third dasboard"
175
+ # },
176
+ # {
177
+ # "id": 2,
178
+ # "playlistId": 1,
179
+ # "type": "dashboard_by_tag",
180
+ # "value": "myTag",
181
+ # "order": 2,
182
+ # "title":"my other dasboard"
183
+ # }
184
+ #]
185
+ #```
186
+
187
+ def playlist_items( playlist_id, multi_result = false )
188
+
189
+ if( playlist_id.is_a?(String) && playlist_id.is_a?(Integer) )
190
+ raise ArgumentError.new(format('wrong type. \'playlist_id\' must be an String (for an playlist name) or an Integer (for an playlist Id), given \'%s\'', playlist_id.class.to_s))
191
+ end
192
+ raise ArgumentError.new('missing \'playlist_id\'') if( playlist_id.size.zero? )
193
+
194
+ tmp_playlists = playlists
195
+
196
+ begin
197
+ status = tmp_playlists.dig('status').to_i
198
+ message = tmp_playlists.dig('message')
199
+
200
+ return tmp_playlists if( status != 200 )
201
+
202
+ data = message.select { |k| k['id'] == playlist_id } if( playlist_id.is_a?(Integer) )
203
+ data = message.select { |k| k['name'] == playlist_id } if( playlist_id.is_a?(String) )
204
+
205
+ return { 'status' => 404, 'message' => 'No Playlist found' } if( !data.is_a?(Array) || data.count.zero? || status.to_i != 200 )
206
+ return { 'status' => 404, 'message' => format('found %d playlists with name %s', data.count, playlist_id ) } if( data.count > 1 && multi_result == false )
207
+
208
+ id = data.first.dig('id')
209
+
210
+ rescue
211
+ return { 'status' => 404, 'message' => 'No Playlists found' } if( playlists.nil? || playlists == false || playlists.dig('status').to_i != 200 )
212
+ end
213
+
214
+ endpoint = "/api/playlists/#{id}/items"
215
+
216
+ result = get( endpoint )
217
+
218
+ return { 'status' => 404, 'message' => 'playlist is empty' } if( result.dig('status') == 404 )
219
+
220
+ result
221
+ end
222
+
223
+ ### Get Playlist dashboards
224
+ #
225
+ #`GET /api/playlists/:id/dashboards`
226
+ #
227
+ #**Example Request**:
228
+ #
229
+ #```bash
230
+ #GET /api/playlists/1/dashboards HTTP/1.1
231
+ #Accept: application/json
232
+ #Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
233
+ #```
234
+ #
235
+ #**Example Response**:
236
+ #
237
+ #```json
238
+ #HTTP/1.1 200
239
+ #Content-Type: application/json
240
+ #[
241
+ # {
242
+ # "id": 3,
243
+ # "title": "my third dasboard",
244
+ # "order": 1,
245
+ # },
246
+ # {
247
+ # "id": 5,
248
+ # "title":"my other dasboard"
249
+ # "order": 2,
250
+ #
251
+ # }
252
+ #]
253
+ #```
254
+
255
+ def playlist_dashboards( playlist_id )
256
+
257
+ raise ArgumentError.new(format('wrong type. \'playlist_id\' must be an Integer, given \'%s\'', playlist_id.class)) unless( playlist_id.is_a?(Integer) )
258
+ raise ArgumentError.new('missing \'playlist_id\'') if( playlist_id.size.zero? )
259
+
260
+ endpoint = format('/api/playlists/%s/dashboards', playlist_id)
261
+
262
+ @logger.debug( "Attempting to get playlist (GET #{endpoint})" ) if @debug
263
+ get(endpoint)
264
+ end
265
+
266
+ # Create a playlist
267
+
268
+ # `POST /api/playlists/`
269
+ #
270
+ #**Example Request**:
271
+ #
272
+ #```bash
273
+ #PUT /api/playlists/1 HTTP/1.1
274
+ #Accept: application/json
275
+ #Content-Type: application/json
276
+ #Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
277
+ # {
278
+ # "name": "my playlist",
279
+ # "interval": "5m",
280
+ # "items": [
281
+ # {
282
+ # "type": "dashboard_by_id",
283
+ # "value": "3",
284
+ # "order": 1,
285
+ # "title":"my third dasboard"
286
+ # },
287
+ # {
288
+ # "type": "dashboard_by_tag",
289
+ # "value": "myTag",
290
+ # "order": 2,
291
+ # "title":"my other dasboard"
292
+ # }
293
+ # ]
294
+ # }
295
+ #```
296
+ #
297
+ #**Example Response**:
298
+ #
299
+ #```json
300
+ #HTTP/1.1 200
301
+ #Content-Type: application/json
302
+ # {
303
+ # "id": 1,
304
+ # "name": "my playlist",
305
+ # "interval": "5m"
306
+ # }
307
+ #```
308
+
309
+ def create_playlist( params )
310
+
311
+ raise ArgumentError.new(format('wrong type. \'params\' must be an Hash, given \'%s\'', params.class.to_s)) unless( params.is_a?(Hash) )
312
+ raise ArgumentError.new('missing \'params\'') if( params.size.zero? )
313
+
314
+ # v, mv = version.values
315
+ # return { 'status' => 404, 'message' => format( 'folder has been supported in Grafana since version 5. you use version %s', v) } if(mv < 5)
316
+
317
+ name = validate( params, required: true , var: 'name' , type: String )
318
+ interval = validate( params, required: true , var: 'interval' , type: String )
319
+ items = validate( params, required: true , var: 'items' , type: Array )
320
+
321
+ return { 'status' => 404, 'message' => 'There are no elements for a playlist' } if(items.count.zero?)
322
+
323
+ payload_items = create_playlist_items(items)
324
+
325
+ payload = {
326
+ name: name,
327
+ interval: interval,
328
+ items: payload_items
329
+ }
330
+ payload.reject!{ |_k, v| v.nil? }
331
+
332
+ endpoint = '/api/playlists'
333
+
334
+ post(endpoint, payload.to_json)
335
+ end
336
+
337
+ ### Update a playlist
338
+ #
339
+ #`PUT /api/playlists/:id`
340
+ #
341
+ #**Example Request**:
342
+ #
343
+ #```bash
344
+ #PUT /api/playlists/1 HTTP/1.1
345
+ #Accept: application/json
346
+ #Content-Type: application/json
347
+ #Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
348
+ # {
349
+ # "name": "my playlist",
350
+ # "interval": "5m",
351
+ # "items": [
352
+ # {
353
+ # "playlistId": 1,
354
+ # "type": "dashboard_by_id",
355
+ # "value": "3",
356
+ # "order": 1,
357
+ # "title":"my third dasboard"
358
+ # },
359
+ # {
360
+ # "playlistId": 1,
361
+ # "type": "dashboard_by_tag",
362
+ # "value": "myTag",
363
+ # "order": 2,
364
+ # "title":"my other dasboard"
365
+ # }
366
+ # ]
367
+ # }
368
+ #```
369
+ #
370
+ #**Example Response**:
371
+ #
372
+ #```json
373
+ #HTTP/1.1 200
374
+ #Content-Type: application/json
375
+ #{
376
+ # "id" : 1,
377
+ # "name": "my playlist",
378
+ # "interval": "5m",
379
+ # "orgId": "my org",
380
+ # "items": [
381
+ # {
382
+ # "id": 1,
383
+ # "playlistId": 1,
384
+ # "type": "dashboard_by_id",
385
+ # "value": "3",
386
+ # "order": 1,
387
+ # "title":"my third dasboard"
388
+ # },
389
+ # {
390
+ # "id": 2,
391
+ # "playlistId": 1,
392
+ # "type": "dashboard_by_tag",
393
+ # "value": "myTag",
394
+ # "order": 2,
395
+ # "title":"my other dasboard"
396
+ # }
397
+ # ]
398
+ #}
399
+ #```
400
+
401
+ def update_playlist( params )
402
+
403
+ raise ArgumentError.new(format('wrong type. \'params\' must be an Hash, given \'%s\'', params.class.to_s)) unless( params.is_a?(Hash) )
404
+ raise ArgumentError.new('missing \'params\'') if( params.size.zero? )
405
+
406
+ playlist_id = validate( params, required: true , var: 'playlist' )
407
+ name = validate( params, required: false, var: 'name' )
408
+ interval = validate( params, required: false, var: 'interval', type: String )
409
+ # organisation = validate( params, required: false, var: 'organisation' )
410
+ items = validate( params, required: false, var: 'items', type: Array )
411
+
412
+ tmp_playlists = playlists
413
+
414
+ data = []
415
+
416
+ begin
417
+ status = tmp_playlists.dig('status').to_i
418
+ message = tmp_playlists.dig('message')
419
+
420
+ return tmp_playlists if( status != 200 )
421
+
422
+ data = message.select { |k| k['id'] == playlist_id } if( playlist_id.is_a?(Integer) )
423
+ data = message.select { |k| k['name'] == playlist_id } if( playlist_id.is_a?(String) )
424
+
425
+ return { 'status' => 404, 'message' => 'no playlist found' } if( !data.is_a?(Array) || data.count.zero? || status.to_i != 200 )
426
+ return { 'status' => 404, 'message' => format('found %d playlists with name %s', data.count, playlist_id ) } if( data.count > 1 && multi_result == false )
427
+
428
+ rescue
429
+ return { 'status' => 404, 'message' => 'no playlists found' } if( playlists.nil? || playlists == false || playlists.dig('status').to_i != 200 )
430
+ end
431
+
432
+ playlist_id = data.first.dig('id')
433
+ # playlist_name = data.first.dig('name')
434
+ payload_items = create_playlist_items(items, playlist_id)
435
+
436
+ payload = {
437
+ id: playlist_id,
438
+ name: name,
439
+ interval: interval,
440
+ items: payload_items
441
+ }
442
+ payload.reject!{ |_k, v| v.nil? }
443
+
444
+ endpoint = format( '/api/playlists/%d', playlist_id )
445
+
446
+ put( endpoint, payload.to_json )
447
+
448
+ end
449
+
450
+
451
+ ### Delete a playlist
452
+ #
453
+ #`DELETE /api/playlists/:id`
454
+ #
455
+ #**Example Request**:
456
+ #
457
+ #```bash
458
+ #DELETE /api/playlists/1 HTTP/1.1
459
+ #Accept: application/json
460
+ #Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
461
+ #```
462
+ #
463
+ #**Example Response**:
464
+ #
465
+ #```json
466
+ #HTTP/1.1 200
467
+ #Content-Type: application/json
468
+ #{}
469
+ #```
470
+
471
+ def delete_playlist(playlist_id, multi_result = false )
472
+
473
+ if( playlist_id.is_a?(String) && playlist_id.is_a?(Integer) )
474
+ raise ArgumentError.new(format('wrong type. \'playlist_id\' must be an String (for an Playlist name) or an Integer (for an Playlist Id), given \'%s\'', playlist_id.class.to_s))
475
+ end
476
+ raise ArgumentError.new('missing \'playlist_id\'') if( playlist_id.size.zero? )
477
+
478
+ tmp_playlists = playlists
479
+
480
+ data = []
481
+
482
+ begin
483
+ status = tmp_playlists.dig('status').to_i
484
+ message = tmp_playlists.dig('message')
485
+
486
+ return tmp_playlists if( status != 200 )
487
+
488
+ data = message.select { |k| k['id'] == playlist_id } if( playlist_id.is_a?(Integer) )
489
+ data = message.select { |k| k['name'] == playlist_id } if( playlist_id.is_a?(String) )
490
+
491
+ return { 'status' => 404, 'message' => 'no playlist found' } if( !data.is_a?(Array) || data.count.zero? || status.to_i != 200 )
492
+ return { 'status' => 404, 'message' => format('found %d playlists with name %s', data.count, playlist_id ) } if( data.count > 1 && multi_result == false )
493
+
494
+ rescue
495
+ return { 'status' => 404, 'message' => 'no playlists found' } if( playlists.nil? || playlists == false || playlists.dig('status').to_i != 200 )
496
+ end
497
+
498
+ if( multi_result == true )
499
+
500
+ result = { 'status' => 0, 'message' => 'under development' }
501
+ data.each do |x|
502
+
503
+ endpoint = format( '/api/playlists/%d', x.dig('id') )
504
+
505
+ begin
506
+ result = delete( endpoint )
507
+ rescue => error
508
+ logger.error( "error: #{error}" )
509
+ end
510
+ end
511
+
512
+ # return result
513
+ else
514
+
515
+ playlist_id = data.first.dig('id')
516
+
517
+ endpoint = format( '/api/playlists/%d', playlist_id )
518
+
519
+ result = delete( endpoint )
520
+
521
+ if(result.dig('status').to_i == 500)
522
+ # check if the playlist exists
523
+ r = playlist( playlist_id )
524
+ return { 'status' => 200, 'message' => 'playlist deleted' } if(r.dig('status').to_i == 404)
525
+ end
526
+
527
+ # return result
528
+ end
529
+
530
+ result
531
+ end
532
+
533
+
534
+ private
535
+ def create_playlist_items( items, playlist_id = nil)
536
+
537
+ playlist_items = []
538
+
539
+ items.each do |r|
540
+ playlist_element = {}
541
+
542
+ if( r['name'] )
543
+
544
+ playlist_name = search_dashboards( query: r['name'] )
545
+ playlist_name_status = playlist_name.dig('status')
546
+
547
+ next unless( playlist_name_status == 200 )
548
+
549
+ playlist_name = playlist_name.dig('message')
550
+ playlist_name_id = playlist_name.first.dig('id')
551
+ playlist_name_title = playlist_name.first.dig('title')
552
+
553
+ playlist_element[:type] = 'dashboard_by_id'
554
+ playlist_element[:value] = playlist_name_id.to_s
555
+ playlist_element[:title] = playlist_name_title
556
+ playlist_element[:playlistId] = playlist_id unless(playlist_id.nil?)
557
+
558
+ elsif( r['id'] )
559
+
560
+ uid = dashboard_by_uid(r['id'])
561
+ uid_status = uid.dig('status')
562
+
563
+ next unless( uid_status == 200 )
564
+
565
+ playlist_element[:type] = 'dashboard_by_id'
566
+ playlist_element[:value] = r['id']
567
+ playlist_element[:playlistId] = playlist_id unless(playlist_id.nil?)
568
+
569
+ elsif( r['tag'] )
570
+
571
+ tags = search_dashboards( tags: r['tag'] )
572
+ tags_status = tags.dig('status')
573
+
574
+ next unless( tags_status == 200 )
575
+
576
+ playlist_element[:type] = 'dashboard_by_tag'
577
+ playlist_element[:value] = r['tag']
578
+ playlist_element[:title] = r['tag']
579
+ playlist_element[:playlistId] = playlist_id unless(playlist_id.nil?)
580
+
581
+ else
582
+ next
583
+ end
584
+
585
+ playlist_element[:order] = r['order'] if(r['order'])
586
+
587
+ playlist_items << playlist_element if(playlist_element.count >= 4)
588
+ end
589
+
590
+ playlist_items
591
+ end
592
+
593
+ end
594
+ end