lm_rest 1.0.1
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 +7 -0
- data/.gitignore +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +8 -0
- data/LICENSE +20 -0
- data/README.md +132 -0
- data/Rakefile +10 -0
- data/api.json +386 -0
- data/bin/console +11 -0
- data/bin/ds_checker.rb +261 -0
- data/bin/setup +7 -0
- data/lib/lm_rest.rb +7 -0
- data/lib/lm_rest/api_client.rb +326 -0
- data/lib/lm_rest/request_params.rb +12 -0
- data/lib/lm_rest/resource.rb +47 -0
- data/lib/lm_rest/version.rb +3 -0
- data/lm_rest.gemspec +34 -0
- metadata +131 -0
data/bin/console
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'lm_rest'
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
require 'pry'
|
11
|
+
Pry.start
|
data/bin/ds_checker.rb
ADDED
@@ -0,0 +1,261 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
#
|
4
|
+
# Based on a script written originally by Matt Dunham
|
5
|
+
#
|
6
|
+
require 'lm_rest'
|
7
|
+
require 'colorize'
|
8
|
+
|
9
|
+
def usage
|
10
|
+
puts "USAGE:\t" + $PROGRAM_NAME + ' account userid passwd datasource_name_or_glob'
|
11
|
+
end
|
12
|
+
|
13
|
+
if ARGV.length == 4
|
14
|
+
@account = ARGV[0]
|
15
|
+
@userid = ARGV[1]
|
16
|
+
@passwd = ARGV[2]
|
17
|
+
@lm = LMRest.new(@account, @userid, @passwd)
|
18
|
+
else
|
19
|
+
usage
|
20
|
+
fail 'Bad arguments.'
|
21
|
+
end
|
22
|
+
|
23
|
+
def dp_type(datapoint)
|
24
|
+
datapoint.postProcessorMethod == 'expression' ? 'complex' : 'normal'
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_datasource_name(datasource)
|
28
|
+
errors = []
|
29
|
+
|
30
|
+
# does the name contain whitespace?
|
31
|
+
errors.push('datasource name contains whitespace') if datasource.name =~ /\s+/
|
32
|
+
|
33
|
+
# does the name end in a trailing dash?
|
34
|
+
errors.push('datasource name has trailing dash') if datasource.name =~ /\-$/
|
35
|
+
|
36
|
+
# does the display name end in a trailing dash?
|
37
|
+
if datasource.displayName =~ /\-$/
|
38
|
+
errors.push('datasource display name has trailing dash')
|
39
|
+
end
|
40
|
+
|
41
|
+
errors
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_datasource_description(datasource)
|
45
|
+
error = nil
|
46
|
+
|
47
|
+
# is the description size less than 10 characters in length?
|
48
|
+
if datasource.description.length < 10
|
49
|
+
error = 'datasource description is empty or sparse'
|
50
|
+
end
|
51
|
+
|
52
|
+
error
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_datapoint_descriptions(datapoints)
|
56
|
+
errors = []
|
57
|
+
|
58
|
+
datapoints.each do |datapoint|
|
59
|
+
if datapoint.description.length < 10
|
60
|
+
errors.push("datapoint \"" + datapoint.name + "\" description is empty or sparse")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
errors
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_datapoint_alerts(datapoints)
|
68
|
+
errors = []
|
69
|
+
|
70
|
+
tokens = [
|
71
|
+
'##HOST##',
|
72
|
+
'##VALUE##',
|
73
|
+
'##DURATION##',
|
74
|
+
'##START##'
|
75
|
+
]
|
76
|
+
|
77
|
+
datapoints.each do |datapoint|
|
78
|
+
# is there a datapoint alert trigger set, but no custom alert message
|
79
|
+
#
|
80
|
+
if (datapoint.alertExpr.size > 0) && (datapoint.alertBody == 0)
|
81
|
+
errors.push("datapoint \"" + datapoint.name +
|
82
|
+
"\" has an alert threshold but no message")
|
83
|
+
end
|
84
|
+
|
85
|
+
# is there a custom alert message on this datapoint?
|
86
|
+
#
|
87
|
+
next unless datapoint.alertBody.size > 0
|
88
|
+
tokens.each do |token|
|
89
|
+
# is this token in the datasource definition?
|
90
|
+
#
|
91
|
+
unless datapoint.alertBody.include? token
|
92
|
+
errors.push("custom alert message on \"" + datapoint.name +
|
93
|
+
"\" datpoint doesn't include token " + token)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
errors
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_datapoint_usage(datapoints, complex_datapoints, graphs, overview_graphs)
|
102
|
+
errors = []
|
103
|
+
datapoint_ok = []
|
104
|
+
|
105
|
+
puts 'Datapoints:'
|
106
|
+
# is an alert trigger set?
|
107
|
+
datapoints.each do |datapoint|
|
108
|
+
if datapoint.alertExpr.size > 0
|
109
|
+
puts ' - ' + dp_type(datapoint) + ' datapoint "' + datapoint.name + '" has alert threshold set'
|
110
|
+
else
|
111
|
+
|
112
|
+
# is this datapoint used in a complex datapoint?
|
113
|
+
complex_datapoints.each do |complex_datapoint|
|
114
|
+
next unless complex_datapoint.postProcessorParam.include? datapoint.name
|
115
|
+
puts(' - ' + dp_type(datapoint) + ' datapoint "' + datapoint.name +
|
116
|
+
'" used in complex datapoint' + complex_datapoint.name)
|
117
|
+
datapoint_ok.push(datapoint.name)
|
118
|
+
break
|
119
|
+
end
|
120
|
+
|
121
|
+
# is this datapoint used in any graphs?
|
122
|
+
graphs.each do |graph|
|
123
|
+
graph.dataPoints.each do |graph_datapoint|
|
124
|
+
next unless datapoint.name == graph_datapoint['name']
|
125
|
+
puts(' - ' + dp_type(datapoint) + ' datapoint "' + datapoint.name +
|
126
|
+
'" used in graph "' + graph.name + '" datapoint')
|
127
|
+
datapoint_ok.push(datapoint.name)
|
128
|
+
break
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
overview_graphs.each do |ograph|
|
133
|
+
ograph.dataPoints.each do |ograph_datapoint|
|
134
|
+
next unless datapoint.name == ograph_datapoint['name']
|
135
|
+
puts(' - ' + dp_type(datapoint) + ' datapoint "' + datapoint.name +
|
136
|
+
'" used in overview graph "' + ograph.name + '" datapoint')
|
137
|
+
datapoint_ok.push(datapoint.name)
|
138
|
+
break
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
unless datapoint_ok.include? datapoint.name
|
143
|
+
errors.push("datapoint \"" + datapoint.name + "\" appears to be unused")
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
separator
|
149
|
+
errors
|
150
|
+
end
|
151
|
+
|
152
|
+
def test_graphs(graphs)
|
153
|
+
errors = []
|
154
|
+
|
155
|
+
puts 'Graphs:'
|
156
|
+
|
157
|
+
display_prios = {}
|
158
|
+
graphs.each do |graph|
|
159
|
+
puts ' - "' + graph.name + '" at display priority ' + graph.displayPrio.to_s
|
160
|
+
# does the y-axis label contain capital letters?
|
161
|
+
if graph.verticalLabel.match(/[A-Z]/)
|
162
|
+
errors.push('graph "' + graph.name +
|
163
|
+
'" has uppercase letters in the y-axis definition (' +
|
164
|
+
graph.verticalLabel + ')')
|
165
|
+
end
|
166
|
+
|
167
|
+
# has this graph priority already been used?
|
168
|
+
if display_prios.include? graph.displayPrio
|
169
|
+
errors.push('graph "' + graph.name + '" is assigned the same display priority (' +
|
170
|
+
graph.displayPrio.to_s + ') as "' +
|
171
|
+
display_prios[graph.displayPrio] + '"')
|
172
|
+
else
|
173
|
+
# no -- store this priority in a hash for further testing
|
174
|
+
display_prios[graph.displayPrio] = graph.name
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
separator
|
179
|
+
errors
|
180
|
+
end
|
181
|
+
|
182
|
+
def test_overview_graphs(overview_graphs)
|
183
|
+
errors = []
|
184
|
+
|
185
|
+
puts 'Overview Graphs:'
|
186
|
+
|
187
|
+
display_prios = {}
|
188
|
+
overview_graphs.each do |ograph|
|
189
|
+
puts ' - "' + ograph.name + '" at display priority ' + ograph.displayPrio.to_s
|
190
|
+
if ograph.verticalLabel.match(/[A-Z]/)
|
191
|
+
errors.push('overview graph "' + ograph.name +
|
192
|
+
'" has uppercase letters in the y-axis definition (' +
|
193
|
+
ograph.verticalLabel + ')')
|
194
|
+
end
|
195
|
+
|
196
|
+
if display_prios.include? ograph.displayPrio
|
197
|
+
errors.push('overview graph "' + ograph.name + '" is assigned the same display priority (' +
|
198
|
+
ograph.displayPrio.to_s + ') as "' +
|
199
|
+
display_prios[ograph.displayPrio] + '"')
|
200
|
+
else
|
201
|
+
display_prios[ograph.displayPrio] = ograph.name
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
separator
|
206
|
+
errors
|
207
|
+
end
|
208
|
+
|
209
|
+
def summarize(datasource, datapoints, graphs, overview_graphs)
|
210
|
+
datapoint_alert_count = 0
|
211
|
+
datapoints.each do |datapoint|
|
212
|
+
datapoint_alert_count += 1 if datapoint.alertExpr.size > 0
|
213
|
+
end
|
214
|
+
|
215
|
+
puts 'Summary:'
|
216
|
+
|
217
|
+
puts " - datasource name:\t#{datasource.name}"
|
218
|
+
puts " - display name:\t#{datasource.displayName}"
|
219
|
+
puts " - applies to:\t\t#{datasource.appliesTo}"
|
220
|
+
puts " - polling interval:\t#{datasource.collectInterval / 60}m"
|
221
|
+
puts " - multipoint instance:\t#{datasource.hasMultiInstances}"
|
222
|
+
puts " - datapoints:\t\t#{datasource.dataPoints.count}"
|
223
|
+
puts " - datapoint alerts:\t#{datapoint_alert_count}"
|
224
|
+
puts " - graphs:\t\t#{graphs.count}"
|
225
|
+
puts " - overview graphs:\t#{overview_graphs.count}"
|
226
|
+
|
227
|
+
separator
|
228
|
+
end
|
229
|
+
|
230
|
+
def propose_fixes(errors)
|
231
|
+
puts 'Proposed Fixes:'
|
232
|
+
|
233
|
+
errors.flatten.each do |error|
|
234
|
+
puts " * #{error}".colorize(:red)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def separator
|
239
|
+
puts '============================='
|
240
|
+
end
|
241
|
+
|
242
|
+
@datasources = @lm.get_datasources(filter: "name:#{ARGV[3]}")
|
243
|
+
|
244
|
+
@datasources.each do |datasource|
|
245
|
+
errors = []
|
246
|
+
datapoints = @lm.get_datapoints(datasource.id)
|
247
|
+
complex_datapoints = datapoints.select { |dp| dp.postProcessorMethod == 'expression' }
|
248
|
+
graphs = @lm.get_graphs(datasource.id)
|
249
|
+
overview_graphs = @lm.get_overview_graphs(datasource.id)
|
250
|
+
|
251
|
+
summarize(datasource, datapoints, graphs, overview_graphs)
|
252
|
+
errors << test_datasource_name(datasource)
|
253
|
+
errors << test_datapoint_descriptions(datapoints)
|
254
|
+
errors << test_datapoint_alerts(datapoints)
|
255
|
+
errors << test_datapoint_usage(datapoints, complex_datapoints, graphs, overview_graphs)
|
256
|
+
errors << test_graphs(graphs)
|
257
|
+
errors << test_overview_graphs(overview_graphs)
|
258
|
+
propose_fixes(errors)
|
259
|
+
|
260
|
+
separator
|
261
|
+
end
|
data/bin/setup
ADDED
data/lib/lm_rest.rb
ADDED
@@ -0,0 +1,326 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'base64'
|
3
|
+
require 'openssl'
|
4
|
+
require 'rest-client'
|
5
|
+
require 'json'
|
6
|
+
require 'lm_rest/resource'
|
7
|
+
require 'lm_rest/request_params'
|
8
|
+
|
9
|
+
module LMRest
|
10
|
+
class APIClient
|
11
|
+
include RequestParams
|
12
|
+
|
13
|
+
ITEMS_SIZE_LIMIT = 1000
|
14
|
+
ITEMS_SIZE_DEFAULT = 50
|
15
|
+
|
16
|
+
BASE_URL_PREFIX = 'https://'
|
17
|
+
BASE_URL_SUFFIX = '.logicmonitor.com/santaba/rest'
|
18
|
+
|
19
|
+
attr_reader :company, :api_url, :access_id
|
20
|
+
|
21
|
+
def initialize(company = nil, access_id = nil, access_key = nil)
|
22
|
+
APIClient.setup
|
23
|
+
@company = company
|
24
|
+
@access_id = access_id
|
25
|
+
@access_key = access_key
|
26
|
+
@api_url = BASE_URL_PREFIX + company + BASE_URL_SUFFIX
|
27
|
+
end
|
28
|
+
|
29
|
+
def uri_to_resource_uri(uri)
|
30
|
+
# Split the URL down to the resource
|
31
|
+
#
|
32
|
+
# Here's an example of the process:
|
33
|
+
# /setting/datasources/1/graphs?key-value&
|
34
|
+
# /setting/datasources/1/graphs
|
35
|
+
# /setting/datasources/
|
36
|
+
# /setting/datasources
|
37
|
+
#
|
38
|
+
uri.split("?")[0].split("/").join("/")
|
39
|
+
end
|
40
|
+
|
41
|
+
def snakerize(string)
|
42
|
+
string.gsub(/::/, '/').
|
43
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
44
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
45
|
+
tr("-", "_").
|
46
|
+
downcase
|
47
|
+
end
|
48
|
+
|
49
|
+
def sign(method, uri, data = nil)
|
50
|
+
|
51
|
+
resource_uri = uri_to_resource_uri(uri)
|
52
|
+
|
53
|
+
time = DateTime.now.strftime('%Q')
|
54
|
+
|
55
|
+
http_method = method.to_s.upcase
|
56
|
+
|
57
|
+
if data.nil? || data.empty?
|
58
|
+
data = ''
|
59
|
+
else
|
60
|
+
data = data.to_json.to_s
|
61
|
+
end
|
62
|
+
|
63
|
+
message = "#{http_method}#{time}#{data}#{resource_uri}"
|
64
|
+
|
65
|
+
signature = Base64.strict_encode64(
|
66
|
+
OpenSSL::HMAC.hexdigest(
|
67
|
+
OpenSSL::Digest.new('sha256'),
|
68
|
+
access_key,
|
69
|
+
message
|
70
|
+
)
|
71
|
+
)
|
72
|
+
|
73
|
+
"LMv1 #{access_id}:#{signature}:#{time}"
|
74
|
+
end
|
75
|
+
|
76
|
+
def request(method, uri, params={})
|
77
|
+
headers = {}
|
78
|
+
headers['Authorization'] = sign(method, uri, params)
|
79
|
+
headers['Content-Type'] = 'application/json'
|
80
|
+
headers['Accept'] = 'application/json, text/javascript'
|
81
|
+
headers['X-version'] = '2'
|
82
|
+
|
83
|
+
url = api_url + uri
|
84
|
+
#puts "URL: " + url
|
85
|
+
#puts headers
|
86
|
+
|
87
|
+
json_params = params.to_json
|
88
|
+
|
89
|
+
case method
|
90
|
+
when :get
|
91
|
+
response = RestClient.get(url, headers)
|
92
|
+
when :post
|
93
|
+
response = RestClient.post(url, json_params, headers)
|
94
|
+
when :put
|
95
|
+
response = RestClient.put(url, json_params, headers)
|
96
|
+
when :delete
|
97
|
+
response = RestClient.delete(url, headers: headers)
|
98
|
+
end
|
99
|
+
|
100
|
+
if response.code != 200
|
101
|
+
puts response.code.to_s + ":" + response.body.to_s
|
102
|
+
raise
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
JSON.parse(response.body)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Handles making multiple requests to the API if pagination is necessary.
|
110
|
+
# Pagination is transparent, and simplifies requests that result in more
|
111
|
+
# than ITEMS_SIZE_LIMIT being returned.
|
112
|
+
#
|
113
|
+
# If you need to walk through resources page-by-page manullay, use the
|
114
|
+
# request() method with the 'offset' and 'size' params
|
115
|
+
#
|
116
|
+
def paginate(uri, params)
|
117
|
+
|
118
|
+
# Hooray for pagination logic!
|
119
|
+
if (params[:size] == 0 || params[:size].nil? || params[:size] > ITEMS_SIZE_LIMIT)
|
120
|
+
# save user-entered size in a param for use later
|
121
|
+
user_size = params[:size]
|
122
|
+
|
123
|
+
# set our size param to the max
|
124
|
+
params[:size] = ITEMS_SIZE_LIMIT
|
125
|
+
|
126
|
+
# Set our offset to grab the first page of results
|
127
|
+
params[:offset] ||= 0
|
128
|
+
|
129
|
+
# make the initial request
|
130
|
+
body = request(:get, uri.call(params), nil)
|
131
|
+
|
132
|
+
# pull the actual items out of the request body and into our
|
133
|
+
# item_collector while we build up the items list
|
134
|
+
item_collector = body['items']
|
135
|
+
|
136
|
+
# The API sends the total number of objects back in the first request.
|
137
|
+
# We need this to determine how many more pages to pull
|
138
|
+
total = body['total']
|
139
|
+
|
140
|
+
# If user didn't pass size param, set it to total
|
141
|
+
# This just means you'll get all items if not specifying a size
|
142
|
+
user_size ||= total
|
143
|
+
|
144
|
+
# If the user passed a size larger than what's available, set it to
|
145
|
+
# total to retrieve all items
|
146
|
+
if user_size > total
|
147
|
+
user_size = total
|
148
|
+
end
|
149
|
+
|
150
|
+
# calculate the remaining number of items (after first request)
|
151
|
+
# then use that to figure out how many more times we need to call
|
152
|
+
# request() to get all the items, then do it
|
153
|
+
pages_remaining = ((user_size - ITEMS_SIZE_LIMIT).to_f/ITEMS_SIZE_LIMIT).ceil
|
154
|
+
|
155
|
+
pages_remaining.times do |page|
|
156
|
+
|
157
|
+
# Increment the offset by the limit to get the next page
|
158
|
+
params[:offset] += ITEMS_SIZE_LIMIT
|
159
|
+
|
160
|
+
# if this is the last page, get the remainder
|
161
|
+
if page == pages_remaining - 1
|
162
|
+
params[:size] = user_size%ITEMS_SIZE_LIMIT
|
163
|
+
else
|
164
|
+
# else, get a whole page
|
165
|
+
params[:size] = ITEMS_SIZE_LIMIT
|
166
|
+
end
|
167
|
+
|
168
|
+
# make a subsequent request with modified params
|
169
|
+
body = request(:get, uri.call(params), nil)
|
170
|
+
|
171
|
+
# add these items to our item_collector
|
172
|
+
item_collector += body['items']
|
173
|
+
end
|
174
|
+
|
175
|
+
body['items'] = item_collector
|
176
|
+
body
|
177
|
+
else
|
178
|
+
# No pagination required, just request the page
|
179
|
+
request(:get, uri.call(params), nil)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def self.process_paths
|
184
|
+
resource_uri = attributes['url']
|
185
|
+
@@api_json[paths].keys.each do |path|
|
186
|
+
|
187
|
+
path.keys.each do |action|
|
188
|
+
case action
|
189
|
+
when 'get'
|
190
|
+
|
191
|
+
uri = lambda { |params| "#{resource_uri}#{RequestParams.parameterize(params)}"}
|
192
|
+
method_name = snakerize(@@api_json['paths'][path][action][operationId])
|
193
|
+
|
194
|
+
unless plural.nil?
|
195
|
+
# Define a method to fetch multiple resources with optional params
|
196
|
+
define_method("get_#{plural}") do |params = {}|
|
197
|
+
Resource.parse paginate(uri, params)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
# Define a method to get one resource by it's id number, with optional
|
202
|
+
# params, thought now that I think about it I'm not sure why you'd pass
|
203
|
+
# params when grabbing just one resource.
|
204
|
+
|
205
|
+
# Some resources are Singletons
|
206
|
+
unless singular.nil?
|
207
|
+
define_method("get_#{singular}") do |*args|
|
208
|
+
case args.size
|
209
|
+
when 0
|
210
|
+
Resource.parse request(:get, "#{resource_uri}", nil)
|
211
|
+
when 1
|
212
|
+
Resource.parse request(:get, "#{resource_uri}/#{args[0]}", nil)
|
213
|
+
when 2
|
214
|
+
Resource.parse request(:get, "#{resource_uri}/#{args[0]}#{RequestParams.parameterize(args[1])}", nil)
|
215
|
+
else
|
216
|
+
raise ArgumentError.new("wrong number for arguments (#{args.count} for 1..2)")
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
when 'add'
|
222
|
+
|
223
|
+
# Define a method to add a new resource to the account
|
224
|
+
define_method("add_#{singular}") do |properties|
|
225
|
+
if properties.class == LMRest::Resource
|
226
|
+
Resource.parse request(:post, "#{resource_uri}", properties.to_h)
|
227
|
+
else
|
228
|
+
Resource.parse request(:post, "#{resource_uri}", properties)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
when 'update'
|
233
|
+
|
234
|
+
# Define a method to update a resource
|
235
|
+
define_method("update_#{singular}") do |id, properties = {}|
|
236
|
+
if id.class == LMRest::Resource
|
237
|
+
Resource.parse request(:put, "#{resource_uri}/#{id.id}", id.to_h)
|
238
|
+
else
|
239
|
+
Resource.parse request(:put, "#{resource_uri}/#{id}", properties)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
when 'delete'
|
244
|
+
|
245
|
+
# Define a method to delete the resource
|
246
|
+
define_method("delete_#{singular}") do |id|
|
247
|
+
if id.class == LMRest::Resource
|
248
|
+
id = id.id
|
249
|
+
Resource.parse request(:delete, "#{resource_uri}/#{id}", nil)
|
250
|
+
else
|
251
|
+
Resource.parse request(:delete, "#{resource_uri}/#{id}", nil)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
def self.define_child_methods(resource_type, attributes)
|
260
|
+
parent_singular = attributes['method_names']['singular']
|
261
|
+
parent_plural = attributes['method_names']['plural']
|
262
|
+
parent_resource_uri = attributes['url']
|
263
|
+
parent_id = attributes['parent_id_key']
|
264
|
+
children = attributes['children']
|
265
|
+
|
266
|
+
children.each do |child_name|
|
267
|
+
if @@api_json[child_name]
|
268
|
+
child = @@api_json[child_name]
|
269
|
+
else
|
270
|
+
raise "Child resource " + child_name + " not defined."
|
271
|
+
end
|
272
|
+
|
273
|
+
child_singular = child['method_names']['singular']
|
274
|
+
child_plural = child['method_names']['plural']
|
275
|
+
child_resource_uri = attributes['url'].split("/").last
|
276
|
+
|
277
|
+
child['actions'].each do |action|
|
278
|
+
case action
|
279
|
+
when 'get'
|
280
|
+
|
281
|
+
define_method("get_#{parent_singular}_#{child_plural}") do |id, params = {}, &block|
|
282
|
+
uri = lambda { |params| "#{parent_resource_uri}/#{id}/#{child['method_names']['plural']}#{RequestParams.parameterize(params)}" }
|
283
|
+
Resource.parse paginate(uri, params)
|
284
|
+
end
|
285
|
+
|
286
|
+
when 'add'
|
287
|
+
when 'update'
|
288
|
+
when 'delete'
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
# Define methods based on the JSON structure
|
295
|
+
def self.setup
|
296
|
+
@@api_definition_path = File.expand_path(File.join(File.dirname(__FILE__), "../../api.json"))
|
297
|
+
@@api_json = JSON.parse(File.read(@@api_definition_path))
|
298
|
+
@@api_json.each do |resource_type, attributes|
|
299
|
+
define_action_methods(resource_type, attributes) if attributes['actions']
|
300
|
+
define_child_methods(resource_type, attributes) if attributes['children']
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
# Ack a down collector, pass the ID and a comment
|
305
|
+
def ack_collector_down(id, comment)
|
306
|
+
if id.class == LMRest::Resource
|
307
|
+
Resource.parse request(:post, "/setting/collectors/#{id.id}/ackdown", {comment: comment})
|
308
|
+
else
|
309
|
+
Resource.parse request(:post, "/setting/collectors/#{id}/ackdown", {comment: comment})
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
# run a report
|
314
|
+
def run_report(id, type = "generateReport")
|
315
|
+
if id.class == LMRest::Resource
|
316
|
+
Resource.parse request(:post, "/functions", {reportId: id.id, type: type})
|
317
|
+
else
|
318
|
+
Resource.parse request(:post, "/functions", {reportId: id, type: type})
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
private
|
323
|
+
|
324
|
+
attr_accessor :access_key
|
325
|
+
end
|
326
|
+
end
|