lm_rest 1.0.6 → 1.0.7
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 +4 -4
- data/.codex +0 -0
- data/README.md +29 -16
- data/api.json +12202 -439
- data/bin/ds_checker.rb +178 -161
- data/bin/graphgrab.rb +41 -0
- data/lib/lm_rest/api_client.rb +409 -170
- data/lib/lm_rest/request_params.rb +13 -5
- data/lib/lm_rest/resource.rb +68 -17
- data/lib/lm_rest/version.rb +1 -1
- data/lm_rest.gemspec +1 -0
- metadata +19 -6
data/lib/lm_rest/api_client.rb
CHANGED
|
@@ -3,10 +3,23 @@ require 'base64'
|
|
|
3
3
|
require 'openssl'
|
|
4
4
|
require 'rest-client'
|
|
5
5
|
require 'json'
|
|
6
|
+
require 'uri'
|
|
6
7
|
require 'lm_rest/resource'
|
|
7
8
|
require 'lm_rest/request_params'
|
|
8
9
|
|
|
9
10
|
module LMRest
|
|
11
|
+
class APIError < StandardError
|
|
12
|
+
attr_reader :status, :body, :headers
|
|
13
|
+
|
|
14
|
+
def initialize(response)
|
|
15
|
+
@status = response.code
|
|
16
|
+
@body = response.body
|
|
17
|
+
@headers = response.headers
|
|
18
|
+
|
|
19
|
+
super("LogicMonitor API request failed with status #{@status}")
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
10
23
|
class APIClient
|
|
11
24
|
include RequestParams
|
|
12
25
|
|
|
@@ -39,19 +52,14 @@ module LMRest
|
|
|
39
52
|
uri.split("?")[0].split("/").join("/")
|
|
40
53
|
end
|
|
41
54
|
|
|
42
|
-
def sign(method, uri, data = nil)
|
|
43
|
-
|
|
55
|
+
def sign(method, uri, data = nil, content_type = 'application/json')
|
|
44
56
|
resource_uri = uri_to_resource_uri(uri)
|
|
45
57
|
|
|
46
58
|
time = DateTime.now.strftime('%Q')
|
|
47
59
|
|
|
48
60
|
http_method = method.to_s.upcase
|
|
49
61
|
|
|
50
|
-
|
|
51
|
-
data = ''
|
|
52
|
-
else
|
|
53
|
-
data = data.to_json.to_s
|
|
54
|
-
end
|
|
62
|
+
data = data_for_signature(data, content_type)
|
|
55
63
|
|
|
56
64
|
message = "#{http_method}#{time}#{data}#{resource_uri}"
|
|
57
65
|
|
|
@@ -66,45 +74,67 @@ module LMRest
|
|
|
66
74
|
"LMv1 #{access_id}:#{signature}:#{time}"
|
|
67
75
|
end
|
|
68
76
|
|
|
69
|
-
def request(method, uri, params=
|
|
70
|
-
headers =
|
|
71
|
-
headers['Authorization'] = sign(method, uri, params)
|
|
72
|
-
headers['Content-Type'] = 'application/json'
|
|
73
|
-
headers['Accept'] = 'application/json, text/javascript'
|
|
74
|
-
headers['X-version'] = '3'
|
|
75
|
-
|
|
77
|
+
def request(method, uri, params = nil, content_type: 'application/json')
|
|
78
|
+
headers = build_headers(method, uri, params, content_type)
|
|
76
79
|
url = api_url + uri
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
80
|
+
payload = serialize_payload(params, content_type)
|
|
81
|
+
|
|
82
|
+
response = execute_request(method.to_sym, url, payload, headers)
|
|
83
|
+
handle_response(response)
|
|
84
|
+
|
|
85
|
+
parse_response(response)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def build_headers(method, uri, params, content_type)
|
|
89
|
+
headers = {
|
|
90
|
+
'Authorization' => sign(method, uri, params, content_type),
|
|
91
|
+
'Accept' => 'application/json, text/javascript',
|
|
92
|
+
'X-version' => '3'
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
headers['Content-Type'] = content_type unless multipart_form?(content_type)
|
|
96
|
+
headers
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def execute_request(method, url, payload, headers)
|
|
100
|
+
case method
|
|
101
|
+
when :get
|
|
102
|
+
RestClient.get(url, headers)
|
|
103
|
+
when :post
|
|
104
|
+
RestClient.post(url, payload, headers)
|
|
105
|
+
when :put
|
|
106
|
+
RestClient.put(url, payload, headers)
|
|
107
|
+
when :patch
|
|
108
|
+
RestClient.patch(url, payload, headers)
|
|
109
|
+
when :delete
|
|
110
|
+
if payload.nil?
|
|
111
|
+
RestClient.delete(url, headers)
|
|
112
|
+
else
|
|
113
|
+
RestClient::Request.execute(method: :delete, url: url, payload: payload, headers: headers)
|
|
92
114
|
end
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
raise
|
|
115
|
+
else
|
|
116
|
+
raise ArgumentError, "unsupported HTTP method: #{method}"
|
|
96
117
|
end
|
|
118
|
+
end
|
|
97
119
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
120
|
+
def handle_response(response)
|
|
121
|
+
raise APIError.new(response) unless response.code.between?(200, 299)
|
|
122
|
+
|
|
123
|
+
@limit = response.headers[:x_rate_limit_limit] || response.headers['x_rate_limit_limit']
|
|
124
|
+
@remaining = response.headers[:x_rate_limit_remaining] || response.headers['x_rate_limit_remaining']
|
|
125
|
+
@window = response.headers[:x_rate_limit_window] || response.headers['x_rate_limit_window']
|
|
126
|
+
end
|
|
102
127
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
@window = response.headers['x_rate_limit_window']
|
|
128
|
+
def parse_response(response)
|
|
129
|
+
return nil if response.body.nil? || response.body.empty?
|
|
106
130
|
|
|
107
|
-
|
|
131
|
+
content_type = response.headers[:content_type] || response.headers['content_type']
|
|
132
|
+
|
|
133
|
+
if content_type.to_s.include?('application/json')
|
|
134
|
+
JSON.parse(response.body)
|
|
135
|
+
else
|
|
136
|
+
response.body
|
|
137
|
+
end
|
|
108
138
|
end
|
|
109
139
|
|
|
110
140
|
# Handles making multiple requests to the API if pagination is necessary.
|
|
@@ -114,70 +144,59 @@ module LMRest
|
|
|
114
144
|
# If you need to walk through resources page-by-page manullay, use the
|
|
115
145
|
# request() method with the 'offset' and 'size' params
|
|
116
146
|
#
|
|
117
|
-
def paginate(uri, params)
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
# If user didn't pass size param, set it to total
|
|
142
|
-
# This just means you'll get all items if not specifying a size
|
|
143
|
-
user_size ||= total
|
|
144
|
-
|
|
145
|
-
# If the user passed a size larger than what's available, set it to
|
|
146
|
-
# total to retrieve all items
|
|
147
|
-
if user_size > total
|
|
148
|
-
user_size = total
|
|
149
|
-
end
|
|
147
|
+
def paginate(uri, params, method = :get, payload = nil, content_type = 'application/json')
|
|
148
|
+
params = normalize_params_hash(params)
|
|
149
|
+
user_size = params[:size]&.to_i
|
|
150
|
+
params[:size] = user_size && user_size < ITEMS_SIZE_LIMIT ? user_size : ITEMS_SIZE_LIMIT
|
|
151
|
+
params[:offset] = (params[:offset] || 0).to_i
|
|
152
|
+
|
|
153
|
+
body = request(method, uri.call(params), payload, content_type: content_type)
|
|
154
|
+
return body unless body.is_a?(Hash) && body.key?('items')
|
|
155
|
+
|
|
156
|
+
total = (body['total'] || body['items'].length).to_i
|
|
157
|
+
user_size = determine_user_size(user_size, total)
|
|
158
|
+
item_collector = body['items'].first(user_size)
|
|
159
|
+
|
|
160
|
+
while item_collector.length < user_size
|
|
161
|
+
params[:offset] += params[:size].to_i
|
|
162
|
+
remaining = user_size - item_collector.length
|
|
163
|
+
params[:size] = [remaining, ITEMS_SIZE_LIMIT].min
|
|
164
|
+
body = request(method, uri.call(params), payload, content_type: content_type)
|
|
165
|
+
break unless body.is_a?(Hash) && body.key?('items')
|
|
166
|
+
break if body['items'].empty?
|
|
167
|
+
|
|
168
|
+
item_collector.concat(body['items'].first(remaining))
|
|
169
|
+
end
|
|
150
170
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
pages_remaining = ((user_size - ITEMS_SIZE_LIMIT).to_f/ITEMS_SIZE_LIMIT).ceil
|
|
171
|
+
body['items'] = item_collector
|
|
172
|
+
body
|
|
173
|
+
end
|
|
155
174
|
|
|
156
|
-
|
|
175
|
+
def determine_user_size(user_size, total)
|
|
176
|
+
user_size ||= total
|
|
177
|
+
user_size > total ? total : user_size
|
|
178
|
+
end
|
|
157
179
|
|
|
158
|
-
|
|
159
|
-
|
|
180
|
+
def self.define_operation_methods(operations)
|
|
181
|
+
operations.each do |method_name, operation|
|
|
182
|
+
define_operation_method(method_name, operation)
|
|
183
|
+
end
|
|
184
|
+
end
|
|
160
185
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
params[:size] = ITEMS_SIZE_LIMIT
|
|
167
|
-
end
|
|
186
|
+
def self.define_operation_method(method_name, operation)
|
|
187
|
+
define_method(method_name) do |*args|
|
|
188
|
+
perform_operation(operation, args)
|
|
189
|
+
end
|
|
190
|
+
end
|
|
168
191
|
|
|
169
|
-
|
|
170
|
-
|
|
192
|
+
def self.define_alias_methods(aliases)
|
|
193
|
+
aliases.each do |alias_name, target_name|
|
|
194
|
+
next if alias_name == target_name
|
|
195
|
+
next unless method_defined?(target_name)
|
|
171
196
|
|
|
172
|
-
|
|
173
|
-
|
|
197
|
+
define_method(alias_name) do |*args|
|
|
198
|
+
public_send(target_name, *args)
|
|
174
199
|
end
|
|
175
|
-
|
|
176
|
-
body['items'] = item_collector
|
|
177
|
-
body
|
|
178
|
-
else
|
|
179
|
-
# No pagination required, just request the page
|
|
180
|
-
request(:get, uri.call(params), nil)
|
|
181
200
|
end
|
|
182
201
|
end
|
|
183
202
|
|
|
@@ -189,78 +208,74 @@ module LMRest
|
|
|
189
208
|
attributes['actions'].each do |action|
|
|
190
209
|
case action
|
|
191
210
|
when 'get'
|
|
211
|
+
define_get_methods(resource_uri, singular, plural)
|
|
212
|
+
when 'add'
|
|
213
|
+
define_add_method(resource_uri, singular)
|
|
214
|
+
when 'update'
|
|
215
|
+
define_update_method(resource_uri, singular)
|
|
216
|
+
when 'delete'
|
|
217
|
+
define_delete_method(resource_uri, singular)
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
192
221
|
|
|
193
|
-
|
|
222
|
+
def self.define_get_methods(resource_uri, singular, plural)
|
|
223
|
+
uri = lambda { |params| "#{resource_uri}#{RequestParams.parameterize(params)}" }
|
|
194
224
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
Resource.parse paginate(uri, params)
|
|
199
|
-
end
|
|
200
|
-
end
|
|
201
|
-
|
|
202
|
-
# Define a method to get one resource by it's id number, with optional
|
|
203
|
-
# params, thought now that I think about it I'm not sure why you'd pass
|
|
204
|
-
# params when grabbing just one resource.
|
|
205
|
-
|
|
206
|
-
# Some resources are Singletons
|
|
207
|
-
unless singular.nil?
|
|
208
|
-
define_method("get_#{singular}") do |*args|
|
|
209
|
-
case args.size
|
|
210
|
-
when 0
|
|
211
|
-
Resource.parse request(:get, "#{resource_uri}", nil)
|
|
212
|
-
when 1
|
|
213
|
-
Resource.parse request(:get, "#{resource_uri}/#{args[0]}", nil)
|
|
214
|
-
when 2
|
|
215
|
-
Resource.parse request(:get, "#{resource_uri}/#{args[0]}#{RequestParams.parameterize(args[1])}", nil)
|
|
216
|
-
else
|
|
217
|
-
raise ArgumentError.new("wrong number for arguments (#{args.count} for 1..2)")
|
|
218
|
-
end
|
|
219
|
-
end
|
|
220
|
-
end
|
|
225
|
+
define_plural_get_method(uri, plural) unless plural.nil?
|
|
226
|
+
define_singular_get_method(resource_uri, singular) unless singular.nil?
|
|
227
|
+
end
|
|
221
228
|
|
|
222
|
-
|
|
229
|
+
def self.define_plural_get_method(uri, plural)
|
|
230
|
+
define_method("get_#{plural}") do |params = {}|
|
|
231
|
+
Resource.parse paginate(uri, params)
|
|
232
|
+
end
|
|
233
|
+
end
|
|
223
234
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
235
|
+
def self.define_singular_get_method(resource_uri, singular)
|
|
236
|
+
define_method("get_#{singular}") do |*args|
|
|
237
|
+
case args.size
|
|
238
|
+
when 0
|
|
239
|
+
Resource.parse request(:get, "#{resource_uri}", nil)
|
|
240
|
+
when 1
|
|
241
|
+
Resource.parse request(:get, "#{resource_uri}/#{args[0]}", nil)
|
|
242
|
+
when 2
|
|
243
|
+
Resource.parse request(:get, "#{resource_uri}/#{args[0]}#{RequestParams.parameterize(args[1])}", nil)
|
|
244
|
+
else
|
|
245
|
+
raise ArgumentError.new("wrong number for arguments (#{args.count} for 1..2)")
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
end
|
|
232
249
|
|
|
233
|
-
|
|
250
|
+
def self.define_add_method(resource_uri, singular)
|
|
251
|
+
define_method("add_#{singular}") do |properties|
|
|
252
|
+
properties_hash = properties.is_a?(LMRest::Resource) ? properties.to_h : properties
|
|
253
|
+
Resource.parse request(:post, "#{resource_uri}", properties_hash)
|
|
254
|
+
end
|
|
255
|
+
end
|
|
234
256
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
end
|
|
242
|
-
end
|
|
257
|
+
def self.define_update_method(resource_uri, singular)
|
|
258
|
+
define_method("update_#{singular}") do |id, properties = {}|
|
|
259
|
+
if id.is_a?(LMRest::Resource)
|
|
260
|
+
properties = id.to_h if properties.empty?
|
|
261
|
+
id = id.id
|
|
262
|
+
end
|
|
243
263
|
|
|
244
|
-
|
|
264
|
+
Resource.parse request(:put, "#{resource_uri}/#{id}", properties)
|
|
265
|
+
end
|
|
266
|
+
end
|
|
245
267
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
else
|
|
252
|
-
Resource.parse request(:delete, "#{resource_uri}/#{id}", nil)
|
|
253
|
-
end
|
|
254
|
-
end
|
|
255
|
-
end
|
|
268
|
+
def self.define_delete_method(resource_uri, singular)
|
|
269
|
+
define_method("delete_#{singular}") do |id, params = {}|
|
|
270
|
+
id = id.id if id.is_a?(LMRest::Resource)
|
|
271
|
+
uri = "#{resource_uri}/#{id}#{RequestParams.parameterize(params)}"
|
|
272
|
+
Resource.parse request(:delete, uri, nil)
|
|
256
273
|
end
|
|
257
274
|
end
|
|
258
275
|
|
|
259
276
|
def self.define_child_methods(resource_type, attributes)
|
|
260
277
|
parent_singular = attributes['method_names']['singular']
|
|
261
|
-
parent_plural = attributes['method_names']['plural']
|
|
262
278
|
parent_resource_uri = attributes['url']
|
|
263
|
-
parent_id = attributes['parent_id_key']
|
|
264
279
|
children = attributes['children']
|
|
265
280
|
|
|
266
281
|
children.each do |child_name|
|
|
@@ -275,39 +290,56 @@ module LMRest
|
|
|
275
290
|
child_resource_uri = attributes['url'].split("/").last
|
|
276
291
|
|
|
277
292
|
child['actions'].each do |action|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
293
|
+
case action
|
|
294
|
+
when 'get'
|
|
281
295
|
define_method("get_#{parent_singular}_#{child_plural}") do |id, params = {}, &block|
|
|
282
296
|
uri = lambda { |params| "#{parent_resource_uri}/#{id}/#{child['method_names']['plural']}#{RequestParams.parameterize(params)}" }
|
|
283
297
|
Resource.parse paginate(uri, params)
|
|
284
298
|
end
|
|
285
299
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
300
|
+
when 'add'
|
|
301
|
+
define_method("add_#{parent_singular}_#{child_singular}") do |parent_id, properties|
|
|
302
|
+
properties_hash = properties.is_a?(LMRest::Resource) ? properties.to_h : properties
|
|
303
|
+
Resource.parse request(:post, "#{parent_resource_uri}/#{parent_id}/#{child_resource_uri}", properties_hash)
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
when 'update'
|
|
307
|
+
define_method("update_#{parent_singular}_#{child_singular}") do |parent_id, child_id, properties = {}|
|
|
308
|
+
Resource.parse request(:put, "#{parent_resource_uri}/#{parent_id}/#{child_resource_uri}/#{child_id}", properties)
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
when 'delete'
|
|
312
|
+
define_method("delete_#{parent_singular}_#{child_singular}") do |parent_id, child_id|
|
|
313
|
+
Resource.parse request(:delete, "#{parent_resource_uri}/#{parent_id}/#{child_resource_uri}/#{child_id}", nil)
|
|
314
|
+
end
|
|
315
|
+
end
|
|
290
316
|
end
|
|
291
317
|
end
|
|
292
318
|
end
|
|
293
319
|
|
|
294
320
|
# Define methods based on the JSON structure
|
|
295
321
|
def self.setup
|
|
322
|
+
return if @api_methods_defined
|
|
323
|
+
|
|
296
324
|
@@api_definition_path = File.expand_path(File.join(File.dirname(__FILE__), "../../api.json"))
|
|
297
325
|
@@api_json = JSON.parse(File.read(@@api_definition_path))
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
326
|
+
|
|
327
|
+
if @@api_json['operations']
|
|
328
|
+
define_operation_methods(@@api_json['operations'])
|
|
329
|
+
define_alias_methods(@@api_json['aliases'] || {})
|
|
330
|
+
else
|
|
331
|
+
@@api_json.each do |resource_type, attributes|
|
|
332
|
+
define_action_methods(resource_type, attributes) if attributes['actions']
|
|
333
|
+
define_child_methods(resource_type, attributes) if attributes['children']
|
|
334
|
+
end
|
|
301
335
|
end
|
|
336
|
+
|
|
337
|
+
@api_methods_defined = true
|
|
302
338
|
end
|
|
303
339
|
|
|
304
340
|
# Ack a down collector, pass the ID and a comment
|
|
305
341
|
def ack_collector_down(id, comment)
|
|
306
|
-
|
|
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
|
|
342
|
+
ack_collector_down_alert_by_id(resource_id(id), comment: comment)
|
|
311
343
|
end
|
|
312
344
|
|
|
313
345
|
# run a report
|
|
@@ -319,8 +351,215 @@ module LMRest
|
|
|
319
351
|
end
|
|
320
352
|
end
|
|
321
353
|
|
|
354
|
+
# Helper to return execution counts for all websites across a time window.
|
|
355
|
+
# Assumes website interval is in minutes and locations/checkpoints is an array
|
|
356
|
+
# or comma-separated list.
|
|
357
|
+
def website_execution_stats(days = 30)
|
|
358
|
+
raise ArgumentError, "days must be positive" if days <= 0
|
|
359
|
+
|
|
360
|
+
websites = get_websites
|
|
361
|
+
|
|
362
|
+
per_site = websites.map do |site|
|
|
363
|
+
interval = website_interval_minutes(site)
|
|
364
|
+
location_count = website_location_count(site)
|
|
365
|
+
next if interval.nil? || interval <= 0
|
|
366
|
+
|
|
367
|
+
executions = (days * 24 * 60.0 / interval * location_count).ceil
|
|
368
|
+
|
|
369
|
+
{
|
|
370
|
+
id: site.respond_to?(:id) ? site.id : nil,
|
|
371
|
+
name: site.respond_to?(:name) ? site.name : nil,
|
|
372
|
+
interval_minutes: interval,
|
|
373
|
+
location_count: location_count,
|
|
374
|
+
executions: executions
|
|
375
|
+
}
|
|
376
|
+
end.compact
|
|
377
|
+
|
|
378
|
+
{
|
|
379
|
+
days: days,
|
|
380
|
+
website_count: per_site.count,
|
|
381
|
+
total_executions: per_site.sum { |row| row[:executions] },
|
|
382
|
+
websites: per_site
|
|
383
|
+
}
|
|
384
|
+
end
|
|
385
|
+
|
|
322
386
|
private
|
|
323
387
|
|
|
324
388
|
attr_accessor :access_key
|
|
389
|
+
|
|
390
|
+
def perform_operation(operation, args)
|
|
391
|
+
path_values, body, query_params = coerce_operation_arguments(operation, args)
|
|
392
|
+
content_type = request_content_type(operation)
|
|
393
|
+
method = operation['method'].to_sym
|
|
394
|
+
uri = lambda { |params| operation_uri(operation, path_values, params) }
|
|
395
|
+
|
|
396
|
+
response =
|
|
397
|
+
if operation_paginated?(operation)
|
|
398
|
+
paginate(uri, query_params, method, body, content_type)
|
|
399
|
+
else
|
|
400
|
+
request(method, uri.call(query_params), body, content_type: content_type)
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
Resource.parse(response)
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
def coerce_operation_arguments(operation, args)
|
|
407
|
+
args = args.dup
|
|
408
|
+
path_param_names = operation['path_params'] || []
|
|
409
|
+
has_body = !!operation['body_param']
|
|
410
|
+
query_params = {}
|
|
411
|
+
inferred_body = nil
|
|
412
|
+
|
|
413
|
+
if has_body
|
|
414
|
+
if args.length > path_param_names.length + 1 && args.last.is_a?(Hash)
|
|
415
|
+
query_params = args.pop
|
|
416
|
+
elsif !operation['body_required'] &&
|
|
417
|
+
args.length == path_param_names.length + 1 &&
|
|
418
|
+
args.last.is_a?(Hash) &&
|
|
419
|
+
query_params_only?(args.last, operation)
|
|
420
|
+
query_params = args.pop
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
if path_param_names.length == 1 &&
|
|
424
|
+
args.length == 1 &&
|
|
425
|
+
resource_like?(args.first) &&
|
|
426
|
+
%w[put patch].include?(operation['method'])
|
|
427
|
+
inferred_body = args.first
|
|
428
|
+
end
|
|
429
|
+
elsif args.length > path_param_names.length && args.last.is_a?(Hash)
|
|
430
|
+
query_params = args.pop
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
path_values = path_param_names.map do
|
|
434
|
+
value = args.shift
|
|
435
|
+
value = inferred_body if value.nil?
|
|
436
|
+
resource_id(value)
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
if path_values.any?(&:nil?)
|
|
440
|
+
raise ArgumentError, "#{operation['name']} requires path params: #{path_param_names.join(', ')}"
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
body = nil
|
|
444
|
+
if has_body
|
|
445
|
+
body_arg = args.shift || inferred_body
|
|
446
|
+
if body_arg.nil? && operation['body_required']
|
|
447
|
+
raise ArgumentError, "#{operation['name']} requires a request body"
|
|
448
|
+
end
|
|
449
|
+
body = resource_to_hash(body_arg) unless body_arg.nil?
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
unless args.empty?
|
|
453
|
+
raise ArgumentError, "wrong number of arguments for #{operation['name']}"
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
[path_values, body, normalize_params_hash(query_params)]
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
def operation_uri(operation, path_values, query_params)
|
|
460
|
+
uri = operation['path'].dup
|
|
461
|
+
|
|
462
|
+
(operation['path_params'] || []).zip(path_values).each do |name, value|
|
|
463
|
+
uri.gsub!("{#{name}}", URI.encode_www_form_component(value.to_s))
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
"#{uri}#{RequestParams.parameterize(query_params)}"
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
def request_content_type(operation)
|
|
470
|
+
Array(operation['consumes']).find { |type| type && !type.empty? } || 'application/json'
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
def operation_paginated?(operation)
|
|
474
|
+
operation['paginated'] || (operation['method'] == 'get' && operation['paginated_response'])
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
def normalize_params_hash(params)
|
|
478
|
+
return {} if params.nil?
|
|
479
|
+
|
|
480
|
+
params.each_with_object({}) do |(key, value), normalized|
|
|
481
|
+
normalized[key.to_sym] = value
|
|
482
|
+
end
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
def query_params_only?(params, operation)
|
|
486
|
+
query_param_names = operation['query_params'] || []
|
|
487
|
+
return false if query_param_names.empty?
|
|
488
|
+
|
|
489
|
+
params.keys.all? { |key| query_param_names.include?(key.to_s) }
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
def resource_like?(value)
|
|
493
|
+
value.is_a?(LMRest::Resource) || value.is_a?(Hash)
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
def resource_id(value)
|
|
497
|
+
case value
|
|
498
|
+
when LMRest::Resource
|
|
499
|
+
value.id if value.respond_to?(:id)
|
|
500
|
+
when Hash
|
|
501
|
+
value[:id] || value['id']
|
|
502
|
+
else
|
|
503
|
+
value
|
|
504
|
+
end
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
def resource_to_hash(value)
|
|
508
|
+
value.is_a?(LMRest::Resource) ? value.to_h : value
|
|
509
|
+
end
|
|
510
|
+
|
|
511
|
+
def data_for_signature(data, content_type)
|
|
512
|
+
return '' if data.nil? || (data.respond_to?(:empty?) && data.empty?)
|
|
513
|
+
return '' if multipart_form?(content_type)
|
|
514
|
+
return data if data.is_a?(String)
|
|
515
|
+
|
|
516
|
+
data.to_json.to_s
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
def serialize_payload(payload, content_type)
|
|
520
|
+
return nil if payload.nil?
|
|
521
|
+
return payload if multipart_form?(content_type)
|
|
522
|
+
return payload if payload.is_a?(String)
|
|
523
|
+
|
|
524
|
+
payload.to_json
|
|
525
|
+
end
|
|
526
|
+
|
|
527
|
+
def multipart_form?(content_type)
|
|
528
|
+
content_type.to_s.include?('multipart/form-data')
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
def website_interval_minutes(site)
|
|
532
|
+
interval_attrs = %i[checkInterval interval pollingInterval testInterval]
|
|
533
|
+
interval_attrs.each do |attr|
|
|
534
|
+
next unless site.respond_to?(attr)
|
|
535
|
+
value = site.send(attr)
|
|
536
|
+
return value.to_f if value
|
|
537
|
+
end
|
|
538
|
+
nil
|
|
539
|
+
end
|
|
540
|
+
|
|
541
|
+
def website_location_count(site)
|
|
542
|
+
location_attrs = %i[checkpoints checkpointIds checkpointId locations locationIds]
|
|
543
|
+
location_attrs.each do |attr|
|
|
544
|
+
next unless site.respond_to?(attr)
|
|
545
|
+
value = site.send(attr)
|
|
546
|
+
count = count_locations(value)
|
|
547
|
+
return count unless count.nil?
|
|
548
|
+
end
|
|
549
|
+
0
|
|
550
|
+
end
|
|
551
|
+
|
|
552
|
+
def count_locations(value)
|
|
553
|
+
case value
|
|
554
|
+
when Array
|
|
555
|
+
value.compact.length
|
|
556
|
+
when String
|
|
557
|
+
value.split(",").map(&:strip).reject(&:empty?).length
|
|
558
|
+
when Numeric
|
|
559
|
+
1
|
|
560
|
+
else
|
|
561
|
+
nil
|
|
562
|
+
end
|
|
563
|
+
end
|
|
325
564
|
end
|
|
326
565
|
end
|