softlayer_api 1.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/LICENSE.textile +9 -0
- data/README.textile +251 -0
- data/examples/accountInformation.rb +41 -0
- data/examples/createTicket.rb +57 -0
- data/examples/openTickets.rb +52 -0
- data/examples/ticket_info.rb +52 -0
- data/lib/softlayer/base.rb +61 -0
- data/lib/softlayer/object_mask_helpers.rb +48 -0
- data/lib/softlayer/service.rb +407 -0
- data/lib/softlayer_api.rb +29 -0
- data/test/SoftLayer_APIParameterFilter.rb +81 -0
- data/test/SoftLayer_Service.rb +376 -0
- data/test/SoftLayer_ToObjectMask.rb +83 -0
- metadata +76 -0
@@ -0,0 +1,61 @@
|
|
1
|
+
# Copyright (c) 2010, SoftLayer Technologies, Inc. All rights reserved.
|
2
|
+
#
|
3
|
+
# Redistribution and use in source and binary forms, with or without
|
4
|
+
# modification, are permitted provided that the following conditions are met:
|
5
|
+
#
|
6
|
+
# * Redistributions of source code must retain the above copyright notice,
|
7
|
+
# this list of conditions and the following disclaimer.
|
8
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
9
|
+
# this list of conditions and the following disclaimer in the documentation
|
10
|
+
# and/or other materials provided with the distribution.
|
11
|
+
# * Neither SoftLayer Technologies, Inc. nor the names of its contributors may
|
12
|
+
# be used to endorse or promote products derived from this software without
|
13
|
+
# specific prior written permission.
|
14
|
+
#
|
15
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
16
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
17
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
18
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
19
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
20
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
21
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
22
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
23
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
24
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
25
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
26
|
+
|
27
|
+
# The SoftLayer Module
|
28
|
+
#
|
29
|
+
# This module is used to provide a namespace for SoftLayer code. It also declares a number of
|
30
|
+
# global variables:
|
31
|
+
# - <tt>$SL_API_USERNAME</tt> - The default username passed by clients to the server for authentication.
|
32
|
+
# Set this if you want to use the same username for all clients and don't want to have to specify it when the client is created
|
33
|
+
# - <tt>$SL_API_KEY</tt> - The default API key passed by clients to the server for authentication.
|
34
|
+
# Set this if you want to use the same api for all clients and don't want to have to specify it when the client is created
|
35
|
+
# - <tt>$SL_API_BASE_URL</tt>- The default URL used to access the SoftLayer API. This defaults to the value of SoftLayer::API_PUBLIC_ENDPOINT
|
36
|
+
#
|
37
|
+
module SoftLayer
|
38
|
+
VERSION = "1.0.0"
|
39
|
+
|
40
|
+
# The base URL of the SoftLayer API's REST-like endpoints available to the public internet.
|
41
|
+
API_PUBLIC_ENDPOINT = 'https://api.softlayer.com/rest/v3/'
|
42
|
+
|
43
|
+
# The base URL of the SoftLayer API's REST-like endpoints available through SoftLayer's private network
|
44
|
+
API_PRIVATE_ENDPOINT = 'https://api.service.softlayer.com/rest/v3/'
|
45
|
+
|
46
|
+
#
|
47
|
+
# These globals can be used to simplify client creation
|
48
|
+
#
|
49
|
+
|
50
|
+
# Set this if you want to provide a default username for each service as it is created.
|
51
|
+
# usernames provided to the service initializer will override the global
|
52
|
+
$SL_API_USERNAME = nil
|
53
|
+
|
54
|
+
# Set this if you want to provide a default api_key for each service as it is
|
55
|
+
# created. API keys provided in the constructor when a service is created will
|
56
|
+
# override the values in this global
|
57
|
+
$SL_API_KEY = nil
|
58
|
+
|
59
|
+
# The base URL used for the SoftLayer API's
|
60
|
+
$SL_API_BASE_URL = SoftLayer::API_PUBLIC_ENDPOINT
|
61
|
+
end # module SoftLayer
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# Copyright (c) 2010, SoftLayer Technologies, Inc. All rights reserved.
|
2
|
+
#
|
3
|
+
# Redistribution and use in source and binary forms, with or without
|
4
|
+
# modification, are permitted provided that the following conditions are met:
|
5
|
+
#
|
6
|
+
# * Redistributions of source code must retain the above copyright notice,
|
7
|
+
# this list of conditions and the following disclaimer.
|
8
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
9
|
+
# this list of conditions and the following disclaimer in the documentation
|
10
|
+
# and/or other materials provided with the distribution.
|
11
|
+
# * Neither SoftLayer Technologies, Inc. nor the names of its contributors may
|
12
|
+
# be used to endorse or promote products derived from this software without
|
13
|
+
# specific prior written permission.
|
14
|
+
#
|
15
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
16
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
17
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
18
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
19
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
20
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
21
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
22
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
23
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
24
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
25
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
26
|
+
|
27
|
+
class Hash
|
28
|
+
def to_sl_object_mask(base = "")
|
29
|
+
# ask the children to convert themselves with the key as the base
|
30
|
+
masked_children = self.map { |key, value| result = value.to_sl_object_mask(key); }.flatten
|
31
|
+
|
32
|
+
# now resolve the children with respect to the base passed in.
|
33
|
+
masked_children.map { |mask_item| mask_item.to_sl_object_mask(base) }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class Array
|
38
|
+
def to_sl_object_mask(base = "")
|
39
|
+
self.map { |item| item.to_sl_object_mask(base) }.flatten
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class String
|
44
|
+
def to_sl_object_mask(base = "")
|
45
|
+
return base if self.empty?
|
46
|
+
base.empty? ? self : "#{base}.#{self}"
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,407 @@
|
|
1
|
+
# Copyright (c) 2010, SoftLayer Technologies, Inc. All rights reserved.
|
2
|
+
#
|
3
|
+
# Redistribution and use in source and binary forms, with or without
|
4
|
+
# modification, are permitted provided that the following conditions are met:
|
5
|
+
#
|
6
|
+
# * Redistributions of source code must retain the above copyright notice,
|
7
|
+
# this list of conditions and the following disclaimer.
|
8
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
9
|
+
# this list of conditions and the following disclaimer in the documentation
|
10
|
+
# and/or other materials provided with the distribution.
|
11
|
+
# * Neither SoftLayer Technologies, Inc. nor the names of its contributors may
|
12
|
+
# be used to endorse or promote products derived from this software without
|
13
|
+
# specific prior written permission.
|
14
|
+
#
|
15
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
16
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
17
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
18
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
19
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
20
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
21
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
22
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
23
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
24
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
25
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
26
|
+
|
27
|
+
require 'rubygems'
|
28
|
+
require 'net/https'
|
29
|
+
require 'json/add/core'
|
30
|
+
|
31
|
+
module SoftLayer
|
32
|
+
# A subclass of Exception with nothing new provided. This simply provides
|
33
|
+
# a unique type for exceptions from the SoftLayer API
|
34
|
+
class SoftLayerAPIException < RuntimeError
|
35
|
+
end
|
36
|
+
|
37
|
+
# An APIParameterFilter is an intermediary object that understands how
|
38
|
+
# to accept the other API parameter filters and carry their values to
|
39
|
+
# method_missing in Service. Instances of this class are created
|
40
|
+
# internally by the Service in it's handling of a method call and you
|
41
|
+
# should not have to create instances of this class directly.
|
42
|
+
#
|
43
|
+
# Instead, to use an API filter, you add a filter method to the call
|
44
|
+
# chain when you call a method through a SoftLayer::Service
|
45
|
+
#
|
46
|
+
# For example, given a SoftLayer::Service instance called "account_service"
|
47
|
+
# you could take advantage of the API filter that identifies a particular
|
48
|
+
# object known to that service using the 'object_with_id" method :
|
49
|
+
#
|
50
|
+
# account_service.object_with_id(91234).getSomeAttribute
|
51
|
+
#
|
52
|
+
# The invocation of object_with_id will cause an instance of this
|
53
|
+
# class to be instantiated with the service as its target.
|
54
|
+
#
|
55
|
+
class APIParameterFilter
|
56
|
+
attr_accessor :target
|
57
|
+
attr_accessor :parameters
|
58
|
+
|
59
|
+
def initialize
|
60
|
+
@parameters = {}
|
61
|
+
end
|
62
|
+
|
63
|
+
def server_object_id
|
64
|
+
self.parameters[:server_object_id]
|
65
|
+
end
|
66
|
+
|
67
|
+
def server_object_mask
|
68
|
+
self.parameters[:object_mask]
|
69
|
+
end
|
70
|
+
|
71
|
+
def object_with_id(value)
|
72
|
+
merged_object = APIParameterFilter.new;
|
73
|
+
merged_object.target = self.target
|
74
|
+
merged_object.parameters = @parameters.merge({ :server_object_id => value })
|
75
|
+
merged_object
|
76
|
+
end
|
77
|
+
|
78
|
+
def object_mask(*args)
|
79
|
+
merged_object = APIParameterFilter.new;
|
80
|
+
merged_object.target = self.target
|
81
|
+
merged_object.parameters = @parameters.merge({ :object_mask => args }) if args && !args.empty?
|
82
|
+
merged_object
|
83
|
+
end
|
84
|
+
|
85
|
+
def method_missing(method_name, *args, &block)
|
86
|
+
return @target.call_softlayer_api_with_params(method_name, self, args, &block)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# = SoftLayer API Service
|
91
|
+
#
|
92
|
+
# Instances of this class represent services in the SoftLayer API.
|
93
|
+
#
|
94
|
+
# You create a service with the name of one of the SoftLayer services
|
95
|
+
# (documented on the http://sldn.softlayer.com web site). Once created
|
96
|
+
# you can use the service to make method calls to the SoftLayer API.
|
97
|
+
#
|
98
|
+
# A typical use might look something like
|
99
|
+
#
|
100
|
+
# account_service = SoftLayer::Service("SoftLayer_Account", :username=>"<your user name here>" :api_key=>"<your api key here>")
|
101
|
+
#
|
102
|
+
# then to invoke a method simply call the service:
|
103
|
+
#
|
104
|
+
# account_service.getOpenTickets
|
105
|
+
# => {... lots of information here representing the list of open tickets ...}
|
106
|
+
#
|
107
|
+
class Service
|
108
|
+
# The name of the service that this object calls. Cannot be emtpy or nil.
|
109
|
+
attr_accessor :service_name
|
110
|
+
|
111
|
+
# A username passed as authentication for each request. Cannot be emtpy or nil.
|
112
|
+
attr_accessor :username
|
113
|
+
|
114
|
+
# An API key passed as part of the authentication of each request. Cannot be emtpy or nil.
|
115
|
+
attr_accessor :api_key
|
116
|
+
|
117
|
+
# The base URL for requests that are passed to the server. Cannot be emtpy or nil.
|
118
|
+
attr_accessor :endpoint_url
|
119
|
+
|
120
|
+
# Initialize an instance of the Client class. You pass in the service name
|
121
|
+
# and optionally hash arguments specifying how the client should access the
|
122
|
+
# SoftLayer API.
|
123
|
+
#
|
124
|
+
# The following symbols can be used as hash arguments to pass options to the constructor:
|
125
|
+
# - <tt>:username</tt> - a non-empty string providing the username to use for requests to the service
|
126
|
+
# - <tt>:api_key</tt> - a non-empty string providing the api key to use for requests to the service
|
127
|
+
# - <tt>:endpoint_url</tt> - a non-empty string providing the endpoint URL to use for requests to the service
|
128
|
+
#
|
129
|
+
# If any of the options above are missing then the constructor will try to use the corresponding
|
130
|
+
# global variable declared in the SoftLayer Module:
|
131
|
+
# - <tt>$SL_API_USERNAME</tt>
|
132
|
+
# - <tt>$SL_API_KEY</tt>
|
133
|
+
# - <tt>$SL_API_BASE_URL</tt>
|
134
|
+
#
|
135
|
+
def initialize(service_name, options = {})
|
136
|
+
raise SoftLayerAPIException.new("Please provide a service name") if service_name.nil? || service_name.empty?
|
137
|
+
self.service_name = service_name;
|
138
|
+
|
139
|
+
# pick up the username provided in options or the default one from the *globals*
|
140
|
+
self.username = options[:username] || $SL_API_USERNAME || ""
|
141
|
+
|
142
|
+
# pick up the api_key provided in options or the default one from the globals
|
143
|
+
self.api_key = options[:api_key] || $SL_API_KEY || ""
|
144
|
+
|
145
|
+
# pick up the url endpoint from options or the default one in the globals OR the
|
146
|
+
# public endpoint
|
147
|
+
self.endpoint_url = options[:endpoint_url] || $SL_API_BASE_URL || API_PUBLIC_ENDPOINT
|
148
|
+
|
149
|
+
if($DEBUG)
|
150
|
+
@method_missing_call_depth = 0
|
151
|
+
end
|
152
|
+
end #initalize
|
153
|
+
|
154
|
+
|
155
|
+
# Use this as part of a method call chain to identify a particular
|
156
|
+
# object as the target of the request. The parameter is the SoftLayer
|
157
|
+
# object identifier you are interested in. For example, this call
|
158
|
+
# would return the ticket whose ID is 35212
|
159
|
+
#
|
160
|
+
# ticket_service.object_with_id(35212).getObject
|
161
|
+
#
|
162
|
+
def object_with_id(object_of_interest)
|
163
|
+
proxy = APIParameterFilter.new
|
164
|
+
proxy.target = self
|
165
|
+
|
166
|
+
return proxy.object_with_id(object_of_interest)
|
167
|
+
end
|
168
|
+
|
169
|
+
# Use this as part of a method call chain to add an object mask to
|
170
|
+
# the request.The arguments to object mask should be the strings
|
171
|
+
# that are the keys of the mask:
|
172
|
+
#
|
173
|
+
# ticket_service.object_mask("createDate", "modifyDate").getObject
|
174
|
+
#
|
175
|
+
# Before being used, the string passed will be url-encoded by this
|
176
|
+
# routine. (i.e. there is no need to url-encode the strings beforehand)
|
177
|
+
#
|
178
|
+
# As an implementation detail, the object_mask becomes part of the
|
179
|
+
# query on the url sent to the API server
|
180
|
+
#
|
181
|
+
def object_mask(*args)
|
182
|
+
proxy = APIParameterFilter.new
|
183
|
+
proxy.target = self
|
184
|
+
|
185
|
+
return proxy.object_mask(*args)
|
186
|
+
end
|
187
|
+
|
188
|
+
# This is the primary mechanism by which requests are made. If you call
|
189
|
+
# the service with a method it doesn't understand, it will send a call to
|
190
|
+
# the endpoint for a method of the same name.
|
191
|
+
#
|
192
|
+
def method_missing(method_name, *args, &block)
|
193
|
+
# During development, if you end up with a stray name in some
|
194
|
+
# code, you can end up in an infinite recursive loop as method_missing
|
195
|
+
# tries to resolve that name (believe me... it happens).
|
196
|
+
# This mechanism looks for what it considers to be an unreasonable call
|
197
|
+
# depth and kills the loop quickly.
|
198
|
+
if($DEBUG)
|
199
|
+
@method_missing_call_depth += 1
|
200
|
+
if @method_missing_call_depth > 3 # 3 is somewhat arbitrary... really the call depth should only ever be 1
|
201
|
+
@method_missing_call_depth = 0
|
202
|
+
raise "stop infinite recursion #{method_name}, #{args.inspect}"
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# if we're in debug mode, we put out a little helpful information
|
207
|
+
puts "SoftLayer::Service#method_missing called #{method_name}, #{args.inspect}" if $DEBUG
|
208
|
+
|
209
|
+
result = call_softlayer_api_with_params(method_name, nil, args, &block);
|
210
|
+
|
211
|
+
if($DEBUG)
|
212
|
+
@method_missing_call_depth -= 1
|
213
|
+
end
|
214
|
+
|
215
|
+
return result
|
216
|
+
end
|
217
|
+
|
218
|
+
# Issue an HTTP request to call the given method from the SoftLayer API with
|
219
|
+
# the parameters and arguments given.
|
220
|
+
#
|
221
|
+
# Parameters are information _about_ the call, the object mask or the
|
222
|
+
# particular object in the SoftLayer API you are calling.
|
223
|
+
#
|
224
|
+
# Arguments are the arguments to the SoftLayer method that you wish to
|
225
|
+
# invoke.
|
226
|
+
#
|
227
|
+
# This is intended to be used in the internal
|
228
|
+
# processing of method_missing and need not be called directly.
|
229
|
+
def call_softlayer_api_with_params(method_name, parameters, args, &block)
|
230
|
+
# find out what URL will invoke the method (with the given parameters)
|
231
|
+
request_url = url_to_call_method(method_name, parameters)
|
232
|
+
|
233
|
+
# construct an HTTP request for that method with the given URL
|
234
|
+
http_request = http_request_for_method(method_name, request_url);
|
235
|
+
http_request.basic_auth(self.username, self.api_key)
|
236
|
+
|
237
|
+
# marshall the arguments into the http_request
|
238
|
+
request_body = marshall_arguments_for_call(args)
|
239
|
+
|
240
|
+
# If you provide arguments to a call that is not supposed to have
|
241
|
+
# arguments, this will print a warning to the console.
|
242
|
+
if request_body && !http_request.request_body_permitted?
|
243
|
+
$stderr.puts("Warning - The HTTP request for #{method_name} does not allow arguments to be passed to the server")
|
244
|
+
else
|
245
|
+
# Otherwise, add the arguments as the body of the request
|
246
|
+
http_request.body = request_body
|
247
|
+
end
|
248
|
+
|
249
|
+
# Send the url request and recover the results. Parse the response (if any)
|
250
|
+
# as JSON
|
251
|
+
json_results = issue_http_request(request_url, http_request, &block)
|
252
|
+
if json_results
|
253
|
+
# The JSON parser for Ruby parses JSON "Text" according to RFC 4627, but
|
254
|
+
# not JSON values. As a result, 'JSON.parse("true")' yields a parsing
|
255
|
+
# exception. To work around this, we force the result JSON text by
|
256
|
+
# including it in Array markers, then take the first element of the
|
257
|
+
# resulting array as the result of the parsing. This should allow values
|
258
|
+
# like true, false, null, and numbers to parse the same way they would in
|
259
|
+
# a browser.
|
260
|
+
parsed_json = JSON.parse("[ #{json_results} ]")[0]
|
261
|
+
|
262
|
+
# if the results indicate an error, convert it into an exception
|
263
|
+
if parsed_json.kind_of?(Hash) && parsed_json['error']
|
264
|
+
raise SoftLayerAPIException.new(parsed_json['error'])
|
265
|
+
end
|
266
|
+
else
|
267
|
+
parsed_json = nil
|
268
|
+
end
|
269
|
+
|
270
|
+
# return the results, if any
|
271
|
+
return parsed_json
|
272
|
+
end
|
273
|
+
|
274
|
+
# Marshall the arguments into a JSON string suitable for the body of
|
275
|
+
# an HTTP message. This is intended to be used in the internal
|
276
|
+
# processing of method_missing and need not be called directly.
|
277
|
+
def marshall_arguments_for_call(args)
|
278
|
+
request_body = nil;
|
279
|
+
|
280
|
+
if(args && !args.empty?)
|
281
|
+
request_body = {"parameters" => args}.to_json
|
282
|
+
end
|
283
|
+
|
284
|
+
return request_body
|
285
|
+
end
|
286
|
+
|
287
|
+
# Given a method name, determine the appropriate HTTP mechanism
|
288
|
+
# for sending a request to execute that method to the server.
|
289
|
+
# and create a Net::HTTP request of that type. This is intended
|
290
|
+
# to be used in the internal processing of method_missing and
|
291
|
+
# need not be called directly.
|
292
|
+
def http_request_for_method(method_name, method_url)
|
293
|
+
content_type_header = {"Content-Type" => "application/json"}
|
294
|
+
|
295
|
+
case method_name.to_s
|
296
|
+
when /^get/
|
297
|
+
Net::HTTP::Get.new(method_url.request_uri())
|
298
|
+
when /^edit/
|
299
|
+
Net::HTTP::Put.new(method_url.request_uri(), content_type_header)
|
300
|
+
when /^delete/
|
301
|
+
Net::HTTP::Delete.new(method_url.request_uri())
|
302
|
+
when /^create/, /^add/, /^remove/, /^findBy/
|
303
|
+
Net::HTTP::Post.new(method_url.request_uri(), content_type_header)
|
304
|
+
else
|
305
|
+
Net::HTTP::Get.new(method_url.request_uri())
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
# Connect to the network and request the content of the resource
|
310
|
+
# specified. This is used to do the actual work of connecting
|
311
|
+
# to the SoftLayer servers and exchange data. This is intended
|
312
|
+
# to be used in the internal processing of method_missing and
|
313
|
+
# need not be called directly.
|
314
|
+
def issue_http_request(request_url, http_request, &block)
|
315
|
+
# create and run an SSL request
|
316
|
+
https = Net::HTTP.new(request_url.host, request_url.port)
|
317
|
+
https.use_ssl = true
|
318
|
+
|
319
|
+
# This line silences an annoying warning message if you're in debug mode
|
320
|
+
https.verify_mode = OpenSSL::SSL::VERIFY_NONE if $DEBUG
|
321
|
+
|
322
|
+
https.start do |http|
|
323
|
+
|
324
|
+
puts "SoftLayer API issuing an HTTP request for #{request_url}" if $DEBUG
|
325
|
+
|
326
|
+
response = https.request(http_request)
|
327
|
+
|
328
|
+
case response
|
329
|
+
when Net::HTTPSuccess
|
330
|
+
return response.body
|
331
|
+
else
|
332
|
+
# We have an error. It might have a meaningful error message
|
333
|
+
# from the server. Check to see if there is a body and whether
|
334
|
+
# or not that body parses as JSON. If it does, then we return
|
335
|
+
# that as a result (assuming it's an error)
|
336
|
+
json_parses = false
|
337
|
+
body = response.body
|
338
|
+
|
339
|
+
begin
|
340
|
+
if body
|
341
|
+
JSON.parse(body)
|
342
|
+
json_parses = true
|
343
|
+
end
|
344
|
+
rescue => json_parse_exception
|
345
|
+
json_parses = false;
|
346
|
+
end
|
347
|
+
|
348
|
+
# Let the HTTP library generate and raise an exception if
|
349
|
+
# the body was empty or could not be parsed as JSON
|
350
|
+
response.value() if !json_parses
|
351
|
+
|
352
|
+
return body
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
# Construct a URL for calling the given method on this endpoint and
|
358
|
+
# expecting a JSON response. This is intended to be used in the internal
|
359
|
+
# processing of method_missing and need not be called directly.
|
360
|
+
def url_to_call_method(method_name, parameters)
|
361
|
+
method_path = method_name.to_s
|
362
|
+
|
363
|
+
# if there's an object ID on the parameters, add that to the URL
|
364
|
+
if(parameters && parameters.server_object_id)
|
365
|
+
method_path = parameters.server_object_id.to_s + "/" + method_path
|
366
|
+
end
|
367
|
+
|
368
|
+
# tag ".json" onto the method path (if it's not already there)
|
369
|
+
method_path.sub!(%r|(\.json){0,1}$|, ".json")
|
370
|
+
|
371
|
+
# put the whole thing together into a URL
|
372
|
+
# (reusing a variation on the clever regular expression above. This one appends a "slash"
|
373
|
+
# to the service name if theres not already one there otherwise. Without it URI.join
|
374
|
+
# doesn't do the right thing)
|
375
|
+
uri = URI.join(self.endpoint_url, self.service_name.sub(%r{/*$},"/"), method_path)
|
376
|
+
|
377
|
+
query_string = nil
|
378
|
+
|
379
|
+
if(parameters && parameters.server_object_mask)
|
380
|
+
mask_value = parameters.server_object_mask.to_sl_object_mask.map { |mask_key| URI.encode(mask_key.to_s.strip) }.join(";")
|
381
|
+
query_string = "objectMask=#{mask_value}"
|
382
|
+
end
|
383
|
+
|
384
|
+
uri.query = query_string
|
385
|
+
|
386
|
+
return uri
|
387
|
+
end
|
388
|
+
|
389
|
+
# Change the username. The username cannot be nil or the empty string.
|
390
|
+
def username= (name)
|
391
|
+
raise SoftLayerAPIException.new("Please provide a username") if name.nil? || name.empty?
|
392
|
+
@username = name.strip
|
393
|
+
end
|
394
|
+
|
395
|
+
# Change the api_key. It cannot be nil or the empty string.
|
396
|
+
def api_key= (new_key)
|
397
|
+
raise SoftLayerAPIException.new("Please provide an api_key") if new_key.nil? || new_key.empty?
|
398
|
+
@api_key = new_key.strip
|
399
|
+
end
|
400
|
+
|
401
|
+
# Change the endpoint_url. It cannot be nil or the empty string.
|
402
|
+
def endpoint_url= (new_url)
|
403
|
+
raise SoftLayerAPIException.new("The endpoint url cannot be nil or empty") if new_url.nil? || new_url.empty?
|
404
|
+
@endpoint_url = new_url.strip
|
405
|
+
end
|
406
|
+
end # class Service
|
407
|
+
end # module SoftLayer
|