softlayer_api 1.0.8 → 2.0.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.
data/lib/softlayer_api.rb CHANGED
@@ -22,4 +22,9 @@
22
22
 
23
23
  require 'softlayer/base'
24
24
  require 'softlayer/object_mask_helpers'
25
- require 'softlayer/service'
25
+ require 'softlayer/APIParameterFilter'
26
+ require 'softlayer/ObjectFilter'
27
+ require 'softlayer/Config'
28
+
29
+ require 'softlayer/Client'
30
+ require 'softlayer/Service'
metadata CHANGED
@@ -1,47 +1,47 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: softlayer_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.8
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - SoftLayer Development Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-11 00:00:00.000000000 Z
11
+ date: 2014-05-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ~>
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.8'
20
- - - ">="
20
+ - - '>='
21
21
  - !ruby/object:Gem::Version
22
22
  version: 1.8.1
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
- - - "~>"
27
+ - - ~>
28
28
  - !ruby/object:Gem::Version
29
29
  version: '1.8'
30
- - - ">="
30
+ - - '>='
31
31
  - !ruby/object:Gem::Version
32
32
  version: 1.8.1
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: rake
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
- - - ">="
37
+ - - '>='
38
38
  - !ruby/object:Gem::Version
39
39
  version: '0'
40
40
  type: :development
41
41
  prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  requirements:
44
- - - ">="
44
+ - - '>='
45
45
  - !ruby/object:Gem::Version
46
46
  version: '0'
47
47
  description: The softlayer_api gem offers a convenient mechanism for invoking the
@@ -51,16 +51,20 @@ executables: []
51
51
  extensions: []
52
52
  extra_rdoc_files: []
53
53
  files:
54
- - LICENSE.textile
55
54
  - README.textile
56
- - examples/accountInformation.rb
57
- - examples/createTicket.rb
58
- - examples/openTickets.rb
59
- - examples/ticket_info.rb
55
+ - LICENSE.textile
56
+ - lib/softlayer/APIParameterFilter.rb
60
57
  - lib/softlayer/base.rb
58
+ - lib/softlayer/Client.rb
59
+ - lib/softlayer/Config.rb
61
60
  - lib/softlayer/object_mask_helpers.rb
62
- - lib/softlayer/service.rb
61
+ - lib/softlayer/ObjectFilter.rb
62
+ - lib/softlayer/Service.rb
63
63
  - lib/softlayer_api.rb
64
+ - examples/account_info.rb
65
+ - examples/create_ticket.rb
66
+ - examples/open_tickets.rb
67
+ - examples/ticket_info.rb
64
68
  homepage: http://sldn.softlayer.com/
65
69
  licenses:
66
70
  - MIT
@@ -71,17 +75,17 @@ require_paths:
71
75
  - lib
72
76
  required_ruby_version: !ruby/object:Gem::Requirement
73
77
  requirements:
74
- - - ">="
78
+ - - '>='
75
79
  - !ruby/object:Gem::Version
76
80
  version: '0'
77
81
  required_rubygems_version: !ruby/object:Gem::Requirement
78
82
  requirements:
79
- - - ">="
83
+ - - '>='
80
84
  - !ruby/object:Gem::Version
81
85
  version: '0'
82
86
  requirements: []
83
87
  rubyforge_project:
84
- rubygems_version: 2.2.2
88
+ rubygems_version: 2.0.14
85
89
  signing_key:
86
90
  specification_version: 4
87
91
  summary: Library for accessing the SoftLayer portal API
@@ -1,467 +0,0 @@
1
- #
2
- # Copyright (c) 2014 SoftLayer Technologies, Inc. All rights reserved.
3
- #
4
- # Permission is hereby granted, free of charge, to any person obtaining a copy
5
- # of this software and associated documentation files (the "Software"), to deal
6
- # in the Software without restriction, including without limitation the rights
7
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
- # copies of the Software, and to permit persons to whom the Software is
9
- # furnished to do so, subject to the following conditions:
10
- #
11
- # The above copyright notice and this permission notice shall be included in
12
- # all copies or substantial portions of the Software.
13
- #
14
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
- # THE SOFTWARE.
21
- #
22
-
23
- require 'rubygems'
24
- require 'net/https'
25
- require 'json/add/core'
26
-
27
- module SoftLayer
28
- # A subclass of Exception with nothing new provided. This simply provides
29
- # a unique type for exceptions from the SoftLayer API
30
- class SoftLayerAPIException < RuntimeError
31
- end
32
-
33
- # An APIParameterFilter is an intermediary object that understands how
34
- # to accept the other API parameter filters and carry their values to
35
- # method_missing in Service. Instances of this class are created
36
- # internally by the Service in it's handling of a method call and you
37
- # should not have to create instances of this class directly.
38
- #
39
- # Instead, to use an API filter, you add a filter method to the call
40
- # chain when you call a method through a SoftLayer::Service
41
- #
42
- # For example, given a SoftLayer::Service instance called "account_service"
43
- # you could take advantage of the API filter that identifies a particular
44
- # object known to that service using the 'object_with_id" method :
45
- #
46
- # account_service.object_with_id(91234).getSomeAttribute
47
- #
48
- # The invocation of object_with_id will cause an instance of this
49
- # class to be instantiated with the service as its target.
50
- #
51
- class APIParameterFilter
52
- attr_accessor :target
53
- attr_accessor :parameters
54
-
55
- def initialize
56
- @parameters = {}
57
- end
58
-
59
- def server_object_id
60
- self.parameters[:server_object_id]
61
- end
62
-
63
- def server_object_mask
64
- self.parameters[:object_mask]
65
- end
66
-
67
- def object_with_id(value)
68
- merged_object = APIParameterFilter.new;
69
- merged_object.target = self.target
70
- merged_object.parameters = @parameters.merge({ :server_object_id => value })
71
- merged_object
72
- end
73
-
74
- def object_mask(*args)
75
- merged_object = APIParameterFilter.new;
76
- merged_object.target = self.target
77
- merged_object.parameters = @parameters.merge({ :object_mask => args }) if args && !args.empty?
78
- merged_object
79
- end
80
-
81
- def result_limit(limit)
82
- merged_object = APIParameterFilter.new;
83
- merged_object.target = self.target
84
- merged_object.parameters = @parameters.merge({ :result_limit => limit })
85
- merged_object
86
- end
87
-
88
- def server_result_limit
89
- self.parameters[:result_limit]
90
- end
91
-
92
- def result_offset(offset)
93
- self.parameters[:result_offset] = offset
94
- self
95
- end
96
-
97
- def server_result_offset
98
- self.parameters[:result_offset]
99
- end
100
-
101
- def method_missing(method_name, *args, &block)
102
- return @target.call_softlayer_api_with_params(method_name, self, args, &block)
103
- end
104
- end
105
-
106
- # = SoftLayer API Service
107
- #
108
- # Instances of this class represent services in the SoftLayer API.
109
- #
110
- # You create a service with the name of one of the SoftLayer services
111
- # (documented on the http://sldn.softlayer.com web site). Once created
112
- # you can use the service to make method calls to the SoftLayer API.
113
- #
114
- # A typical use might look something like
115
- #
116
- # account_service = SoftLayer::Service("SoftLayer_Account", :username=>"<your user name here>" :api_key=>"<your api key here>")
117
- #
118
- # then to invoke a method simply call the service:
119
- #
120
- # account_service.getOpenTickets
121
- # => {... lots of information here representing the list of open tickets ...}
122
- #
123
- class Service
124
- # The name of the service that this object calls. Cannot be emtpy or nil.
125
- attr_accessor :service_name
126
-
127
- # A username passed as authentication for each request. Cannot be emtpy or nil.
128
- attr_accessor :username
129
-
130
- # An API key passed as part of the authentication of each request. Cannot be emtpy or nil.
131
- attr_accessor :api_key
132
-
133
- # The base URL for requests that are passed to the server. Cannot be emtpy or nil.
134
- attr_accessor :endpoint_url
135
-
136
- # The User-Agent header sent to SoftLayer API.
137
- attr_accessor :user_agent
138
-
139
- # Initialize an instance of the Client class. You pass in the service name
140
- # and optionally hash arguments specifying how the client should access the
141
- # SoftLayer API.
142
- #
143
- # The following symbols can be used as hash arguments to pass options to the constructor:
144
- # - <tt>:username</tt> - a non-empty string providing the username to use for requests to the service
145
- # - <tt>:api_key</tt> - a non-empty string providing the api key to use for requests to the service
146
- # - <tt>:endpoint_url</tt> - a non-empty string providing the endpoint URL to use for requests to the service
147
- #
148
- # If any of the options above are missing then the constructor will try to use the corresponding
149
- # global variable declared in the SoftLayer Module:
150
- # - <tt>$SL_API_USERNAME</tt>
151
- # - <tt>$SL_API_KEY</tt>
152
- # - <tt>$SL_API_BASE_URL</tt>
153
- #
154
- def initialize(service_name, options = {})
155
- raise SoftLayerAPIException.new("Please provide a service name") if service_name.nil? || service_name.empty?
156
- self.service_name = service_name;
157
-
158
- # pick up the username provided in options or the default one from the *globals*
159
- self.username = options[:username] || $SL_API_USERNAME || ""
160
-
161
- # pick up the api_key provided in options or the default one from the globals
162
- self.api_key = options[:api_key] || $SL_API_KEY || ""
163
-
164
- # pick up the url endpoint from options or the default one in the globals OR the
165
- # public endpoint
166
- self.endpoint_url = options[:endpoint_url] || $SL_API_BASE_URL || API_PUBLIC_ENDPOINT
167
-
168
- @user_agent = {"User-Agent" => options[:user_agent] || "SoftLayer API Ruby Client #{SoftLayer::VERSION}"}
169
-
170
- if($DEBUG)
171
- @method_missing_call_depth = 0
172
- end
173
- end #initalize
174
-
175
- # Use this to set the user agent string for this API client.
176
- def user_agent=(set)
177
- self.user_agent['User-Agent'] = set
178
- end
179
-
180
- # Use this as part of a method call chain to identify a particular
181
- # object as the target of the request. The parameter is the SoftLayer
182
- # object identifier you are interested in. For example, this call
183
- # would return the ticket whose ID is 35212
184
- #
185
- # ticket_service.object_with_id(35212).getObject
186
- #
187
- def object_with_id(object_of_interest)
188
- proxy = APIParameterFilter.new
189
- proxy.target = self
190
-
191
- return proxy.object_with_id(object_of_interest)
192
- end
193
-
194
- # Use this as part of a method call chain to add an object mask to
195
- # the request.The arguments to object mask should be the strings
196
- # that are the keys of the mask:
197
- #
198
- # ticket_service.object_mask("createDate", "modifyDate").getObject
199
- #
200
- # Before being used, the string passed will be url-encoded by this
201
- # routine. (i.e. there is no need to url-encode the strings beforehand)
202
- #
203
- # As an implementation detail, the object_mask becomes part of the
204
- # query on the url sent to the API server
205
- #
206
- def object_mask(*args)
207
- proxy = APIParameterFilter.new
208
- proxy.target = self
209
-
210
- return proxy.object_mask(*args)
211
- end
212
-
213
- def result_limit(limit)
214
- proxy = APIParameterFilter.new
215
- proxy.target = self
216
- return proxy.result_limit(limit)
217
- end
218
-
219
- def result_offset(offset)
220
- proxy = APIParameterFilter.new
221
- proxy.target = self
222
- return proxy.result_offset(offset)
223
- end
224
-
225
- # This is the primary mechanism by which requests are made. If you call
226
- # the service with a method it doesn't understand, it will send a call to
227
- # the endpoint for a method of the same name.
228
- #
229
- def method_missing(method_name, *args, &block)
230
- # During development, if you end up with a stray name in some
231
- # code, you can end up in an infinite recursive loop as method_missing
232
- # tries to resolve that name (believe me... it happens).
233
- # This mechanism looks for what it considers to be an unreasonable call
234
- # depth and kills the loop quickly.
235
- if($DEBUG)
236
- @method_missing_call_depth += 1
237
- if @method_missing_call_depth > 3 # 3 is somewhat arbitrary... really the call depth should only ever be 1
238
- @method_missing_call_depth = 0
239
- raise "stop infinite recursion #{method_name}, #{args.inspect}"
240
- end
241
- end
242
-
243
- # if we're in debug mode, we put out a little helpful information
244
- puts "SoftLayer::Service#method_missing called #{method_name}, #{args.inspect}" if $DEBUG
245
-
246
- result = call_softlayer_api_with_params(method_name, nil, args, &block);
247
-
248
- if($DEBUG)
249
- @method_missing_call_depth -= 1
250
- end
251
-
252
- return result
253
- end
254
-
255
- # Issue an HTTP request to call the given method from the SoftLayer API with
256
- # the parameters and arguments given.
257
- #
258
- # Parameters are information _about_ the call, the object mask or the
259
- # particular object in the SoftLayer API you are calling.
260
- #
261
- # Arguments are the arguments to the SoftLayer method that you wish to
262
- # invoke.
263
- #
264
- # This is intended to be used in the internal
265
- # processing of method_missing and need not be called directly.
266
- def call_softlayer_api_with_params(method_name, parameters, args, &block)
267
- # find out what URL will invoke the method (with the given parameters)
268
- request_url = url_to_call_method(method_name, parameters)
269
-
270
- # marshall the arguments into the http_request
271
- request_body = marshall_arguments_for_call(args)
272
-
273
- # construct an HTTP request for that method with the given URL
274
- http_request = http_request_for_method(method_name, request_url, request_body);
275
- http_request.basic_auth(self.username, self.api_key)
276
-
277
- # Send the url request and recover the results. Parse the response (if any)
278
- # as JSON
279
- json_results = issue_http_request(request_url, http_request, &block)
280
- if json_results
281
- # The JSON parser for Ruby parses JSON "Text" according to RFC 4627, but
282
- # not JSON values. As a result, 'JSON.parse("true")' yields a parsing
283
- # exception. To work around this, we force the result JSON text by
284
- # including it in Array markers, then take the first element of the
285
- # resulting array as the result of the parsing. This should allow values
286
- # like true, false, null, and numbers to parse the same way they would in
287
- # a browser.
288
- parsed_json = JSON.parse("[ #{json_results} ]")[0]
289
-
290
- # if the results indicate an error, convert it into an exception
291
- if parsed_json.kind_of?(Hash) && parsed_json['error']
292
- raise SoftLayerAPIException.new(parsed_json['error'])
293
- end
294
- else
295
- parsed_json = nil
296
- end
297
-
298
- # return the results, if any
299
- return parsed_json
300
- end
301
-
302
- # Marshall the arguments into a JSON string suitable for the body of
303
- # an HTTP message. This is intended to be used in the internal
304
- # processing of method_missing and need not be called directly.
305
- def marshall_arguments_for_call(args)
306
- request_body = nil;
307
-
308
- if(args && !args.empty?)
309
- request_body = {"parameters" => args}.to_json
310
- end
311
-
312
- return request_body
313
- end
314
-
315
- # Given a method name, determine the appropriate HTTP mechanism
316
- # for sending a request to execute that method to the server.
317
- # and create a Net::HTTP request of that type. This is intended
318
- # to be used in the internal processing of method_missing and
319
- # need not be called directly.
320
- def http_request_for_method(method_name, method_url, request_body = nil)
321
- content_type_header = {"Content-Type" => "application/json"}
322
-
323
- # This is a workaround for a potential problem that arises from mis-using the
324
- # API. If you call SoftLayer_Virtual_Guest and you call the getObject method
325
- # but pass a virtual guest as a parameter, what happens is the getObject method
326
- # is called through an HTTP POST verb and the API creates a new CCI that is a copy
327
- # of the one you passed in.
328
- #
329
- # The counter-intuitive creation of a new CCI is unexpected and, even worse,
330
- # is something you can be billed for. To prevent that, we ignore the request
331
- # body on a "getObject" call and print out a warning.
332
- if (method_name == :getObject) && (nil != request_body) then
333
- $stderr.puts "Warning - The getObject method takes no parameters. The parameters you have provided will be ignored."
334
- request_body = nil
335
- end
336
-
337
- if request_body && !request_body.empty?
338
- url_request = Net::HTTP::Post.new(method_url.request_uri(), content_type_header.merge(self.user_agent))
339
- else
340
- url_request = Net::HTTP::Get.new(method_url.request_uri(), self.user_agent)
341
- end
342
-
343
- # This warning should be obsolete as we should be using POST if the user
344
- # has provided parameters. I'm going to leave it in, however, on the off
345
- # chance that it catches a case we aren't expecting.
346
- if request_body && !url_request.request_body_permitted?
347
- $stderr.puts("Warning - The HTTP request for #{method_name} does not allow arguments to be passed to the server")
348
- else
349
- # Otherwise, add the arguments as the body of the request
350
- url_request.body = request_body
351
- end
352
-
353
- url_request
354
- end
355
-
356
- # Connect to the network and request the content of the resource
357
- # specified. This is used to do the actual work of connecting
358
- # to the SoftLayer servers and exchange data. This is intended
359
- # to be used in the internal processing of method_missing and
360
- # need not be called directly.
361
- def issue_http_request(request_url, http_request, &block)
362
- # create and run an SSL request
363
- https = Net::HTTP.new(request_url.host, request_url.port)
364
- https.use_ssl = (request_url.scheme == "https")
365
-
366
- # This line silences an annoying warning message if you're in debug mode
367
- https.verify_mode = OpenSSL::SSL::VERIFY_NONE if $DEBUG
368
-
369
- https.start do |http|
370
-
371
- puts "SoftLayer API issuing an HTTP request for #{request_url}" if $DEBUG
372
-
373
- response = https.request(http_request)
374
-
375
- case response
376
- when Net::HTTPSuccess
377
- return response.body
378
- else
379
- # We have an error. It might have a meaningful error message
380
- # from the server. Check to see if there is a body and whether
381
- # or not that body parses as JSON. If it does, then we return
382
- # that as a result (assuming it's an error)
383
- json_parses = false
384
- body = response.body
385
-
386
- begin
387
- if body
388
- JSON.parse(body)
389
- json_parses = true
390
- end
391
- rescue => json_parse_exception
392
- json_parses = false;
393
- end
394
-
395
- # Let the HTTP library generate and raise an exception if
396
- # the body was empty or could not be parsed as JSON
397
- response.value() if !json_parses
398
-
399
- return body
400
- end
401
- end
402
- end
403
-
404
- # Construct a URL for calling the given method on this endpoint and
405
- # expecting a JSON response. This is intended to be used in the internal
406
- # processing of method_missing and need not be called directly.
407
- def url_to_call_method(method_name, parameters)
408
- method_path = method_name.to_s
409
-
410
- # if there's an object ID on the parameters, add that to the URL
411
- if(parameters && parameters.server_object_id)
412
- method_path = parameters.server_object_id.to_s + "/" + method_path
413
- end
414
-
415
- # tag ".json" onto the method path (if it's not already there)
416
- method_path.sub!(%r|(\.json){0,1}$|, ".json")
417
-
418
- # put the whole thing together into a URL
419
- # (reusing a variation on the clever regular expression above. This one appends a "slash"
420
- # to the service name if theres not already one there otherwise. Without it URI.join
421
- # doesn't do the right thing)
422
- uri = URI.join(self.endpoint_url, self.service_name.sub(%r{/*$},"/"), method_path)
423
-
424
- query_string = nil
425
-
426
- if(parameters && parameters.server_object_mask)
427
- mask_value = parameters.server_object_mask.to_sl_object_mask.map { |mask_key| URI.encode(mask_key.to_s.strip) }.join(";")
428
- query_string = "objectMask=#{mask_value}"
429
- end
430
-
431
- if (parameters && parameters.server_result_limit)
432
- resultLimit = parameters.server_result_limit
433
- resultOffset = parameters.server_result_offset
434
- resultOffset = 0 if resultOffset.nil?
435
- limit_string = "resultLimit=#{resultOffset},#{resultLimit}"
436
- if query_string.nil?
437
- query_string = limit_string
438
- else
439
- query_string << "&#{limit_string}"
440
- end
441
- end
442
-
443
- uri.query = query_string
444
-
445
- return uri
446
- end
447
-
448
- # Change the username. The username cannot be nil or the empty string.
449
- def username= (name)
450
- raise SoftLayerAPIException.new("Please provide a username") if name.nil? || name.empty?
451
- @username = name.strip
452
- end
453
-
454
- # Change the api_key. It cannot be nil or the empty string.
455
- def api_key= (new_key)
456
- raise SoftLayerAPIException.new("Please provide an api_key") if new_key.nil? || new_key.empty?
457
- @api_key = new_key.strip
458
- end
459
-
460
- # Change the endpoint_url. It cannot be nil or the empty string.
461
- def endpoint_url= (new_url)
462
- raise SoftLayerAPIException.new("The endpoint url cannot be nil or empty") if new_url.nil? || new_url.empty?
463
- @endpoint_url = new_url.strip
464
- end
465
- end # class Service
466
- end # module SoftLayer
467
-