lm_rest 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|